Protected
Optional
_seedProtected
intervalProtected
paramGet the parameters of the algorithm.
Set the parameters of the algorithm.
Partial
0<request_retention<=1,Requested retention rate
https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45
The formula used is: $$I(r,s) = (r^{\frac{1}{DECAY}} - 1) / FACTOR \times s$$
Requested retention rate should be in the range (0,1]
Optional
mode: StrategyModeCard to be processed
Current time or scheduled time
Should the review count information(reps,lapses) be reset. (Optional)
Optional
afterHandler: ((recordLogItem: RecordLogItem) => R)Convert the result to another type. (Optional)
const now = new Date();
const f = fsrs();
const emptyCard = createEmptyCard(now);
const scheduling_cards = f.repeat(emptyCard, now);
const { card, log } = scheduling_cards[Rating.Hard];
const forgetCard = f.forget(card, new Date(), true);
interface RepeatRecordLog {
card: CardUnChecked; //see method: createEmptyCard
log: RevLogUnchecked; //see method: fsrs.repeat()
}
function forgetAfterHandler(recordLogItem: RecordLogItem): RepeatRecordLog {
return {
card: {
...(recordLogItem.card as Card & { cid: string }),
due: recordLogItem.card.due.getTime(),
state: State[recordLogItem.card.state] as StateType,
last_review: recordLogItem.card.last_review
? recordLogItem.card.last_review!.getTime()
: null,
},
log: {
...recordLogItem.log,
cid: (recordLogItem.card as Card & { cid: string }).cid,
due: recordLogItem.log.due.getTime(),
review: recordLogItem.log.review.getTime(),
state: State[recordLogItem.log.state] as StateType,
rating: Rating[recordLogItem.log.rating] as RatingType,
},
};
}
const now = new Date();
const f = fsrs();
const emptyCardFormAfterHandler = createEmptyCard(now, cardAfterHandler); //see method: createEmptyCard
const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now, repeatAfterHandler); //see method: fsrs.repeat()
const { card } = repeatFormAfterHandler[Rating.Hard];
const forgetFromAfterHandler = f.forget(card, date_scheduler(now, 1, true), false, forgetAfterHandler);
The formula used is : $$R(t,S) = (1 + \text{FACTOR} \times \frac{t}{9 \cdot S})^{\text{DECAY}}$$
t days since the last review
Stability (interval when R=90%)
r Retrievability (probability of recall)
The formula used is : $$D_0(G) = w_4 - e^{(G-1) \cdot w_5} + 1 $$ $$D_0 = \min \lbrace \max \lbrace D_0(G),1 \rbrace,10 \rbrace$$ where the $$D_0(1)=w_4$$ when the first rating is good.
Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
Difficulty $$D \in [1,10]$$
The formula used is : $$ S_0(G) = w_{G-1}$$ $$S_0 = \max \lbrace S_0,0.1\rbrace $$
Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
Stability (interval when R=90%)
Display the collection of cards and logs for the card scheduled at the current time, after applying a specific grade rating.
Card to be processed
Current time or scheduled time
Rating of the review (Again, Hard, Good, Easy)
Optional
afterHandler: ((recordLog: RecordLogItem) => R)Convert the result to another type. (Optional)
const card: Card = createEmptyCard(new Date());
const f = fsrs();
const recordLogItem = f.next(card, new Date(), Rating.Again);
interface RevLogUnchecked
extends Omit<ReviewLog, "due" | "review" | "state" | "rating"> {
cid: string;
due: Date | number;
state: StateType;
review: Date | number;
rating: RatingType;
}
interface NextRecordLog {
card: CardUnChecked; //see method: createEmptyCard
log: RevLogUnchecked;
}
function nextAfterHandler(recordLogItem: RecordLogItem) {
const recordItem = {
card: {
...(recordLogItem.card as Card & { cid: string }),
due: recordLogItem.card.due.getTime(),
state: State[recordLogItem.card.state] as StateType,
last_review: recordLogItem.card.last_review
? recordLogItem.card.last_review!.getTime()
: null,
},
log: {
...recordLogItem.log,
cid: (recordLogItem.card as Card & { cid: string }).cid,
due: recordLogItem.log.due.getTime(),
review: recordLogItem.log.review.getTime(),
state: State[recordLogItem.log.state] as StateType,
rating: Rating[recordLogItem.log.rating] as RatingType,
},
};
return recordItem
}
const card: Card = createEmptyCard(new Date(), cardAfterHandler); //see method: createEmptyCard
const f = fsrs();
const recordLogItem = f.repeat(card, new Date(), Rating.Again, nextAfterHandler);
The formula used is : $$\text{delta}_d = -w_6 \cdot (g - 3)$$ $$\text{next}_d = D + \text{linear damping}(\text{delta}_d , D)$$ $$D^\prime(D,R) = w_7 \cdot D_0(4) +(1 - w_7) \cdot \text{next}_d$$
Difficulty $$D \in [1,10]$$
Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
$$\text{next}_D$$
The formula used is : $$S^\prime_f(D,S,R) = w_{11}\cdot D^{-w_{12}}\cdot ((S+1)^{w_{13}}-1) \cdot e^{w_{14}\cdot(1-R)}$$ enable_short_term = true : $$S^\prime_f \in \min \lbrace \max \lbrace S^\prime_f,0.01\rbrace, \frac{S}{e^{w_{17} \cdot w_{18}}} \rbrace$$ enable_short_term = false : $$S^\prime_f \in \min \lbrace \max \lbrace S^\prime_f,0.01\rbrace, S \rbrace$$
Difficulty D \in [1,10]
Stability (interval when R=90%)
Retrievability (probability of recall)
S^\prime_f new stability after forgetting
Stability (interval when R=90%)
t days since the last review
The formula used is : FSRSAlgorithm.calculate_interval_modifier
The formula used is : $$S^\prime_r(D,S,R,G) = S\cdot(e^{w_8}\cdot (11-D)\cdot S^{-w_9}\cdot(e^{w_{10}\cdot(1-R)}-1)\cdot w_{15}(\text{if} G=2) \cdot w_{16}(\text{if} G=4)+1)$$
Difficulty D \in [1,10]
Stability (interval when R=90%)
Retrievability (probability of recall)
Grade (Rating[0.again,1.hard,2.good,3.easy])
S^\prime_r new stability after recall
The formula used is : $$S^\prime_s(S,G) = S \cdot e^{w_{17} \cdot (G-3+w_{18})}$$
Stability (interval when R=90%)
Grade (Rating[0.again,1.hard,2.good,3.easy])
Protected
params_Display the collection of cards and logs for the four scenarios after scheduling the card at the current time.
const card: Card = createEmptyCard(new Date());
const f = fsrs();
const recordLog = f.repeat(card, new Date());
interface RevLogUnchecked
extends Omit<ReviewLog, "due" | "review" | "state" | "rating"> {
cid: string;
due: Date | number;
state: StateType;
review: Date | number;
rating: RatingType;
}
interface RepeatRecordLog {
card: CardUnChecked; //see method: createEmptyCard
log: RevLogUnchecked;
}
function repeatAfterHandler(recordLog: RecordLog) {
const record: { [key in Grade]: RepeatRecordLog } = {} as {
[key in Grade]: RepeatRecordLog;
};
for (const grade of Grades) {
record[grade] = {
card: {
...(recordLog[grade].card as Card & { cid: string }),
due: recordLog[grade].card.due.getTime(),
state: State[recordLog[grade].card.state] as StateType,
last_review: recordLog[grade].card.last_review
? recordLog[grade].card.last_review!.getTime()
: null,
},
log: {
...recordLog[grade].log,
cid: (recordLog[grade].card as Card & { cid: string }).cid,
due: recordLog[grade].log.due.getTime(),
review: recordLog[grade].log.review.getTime(),
state: State[recordLog[grade].log.state] as StateType,
rating: Rating[recordLog[grade].log.rating] as RatingType,
},
};
}
return record;
}
const card: Card = createEmptyCard(new Date(), cardAfterHandler); //see method: createEmptyCard
const f = fsrs();
const recordLog = f.repeat(card, new Date(), repeatAfterHandler);
Reschedules the current card and returns the rescheduled collections and reschedule item.
The type of the record log item.
The current card to be rescheduled.
The array of FSRSHistory objects representing the reviews.
The optional reschedule options.
const f = fsrs()
const grades: Grade[] = [Rating.Good, Rating.Good, Rating.Good, Rating.Good]
const reviews_at = [
new Date(2024, 8, 13),
new Date(2024, 8, 13),
new Date(2024, 8, 17),
new Date(2024, 8, 28),
]
const reviews: FSRSHistory[] = []
for (let i = 0; i < grades.length; i++) {
reviews.push({
rating: grades[i],
review: reviews_at[i],
})
}
const results_short = scheduler.reschedule(
createEmptyCard(),
reviews,
{
skipManual: false,
}
)
console.log(results_short)
const now = new Date();
const f = fsrs();
const emptyCardFormAfterHandler = createEmptyCard(now);
const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now);
const { card, log } = repeatFormAfterHandler[Rating.Hard];
const rollbackFromAfterHandler = f.rollback(card, log);
const now = new Date();
const f = fsrs();
const emptyCardFormAfterHandler = createEmptyCard(now, cardAfterHandler); //see method: createEmptyCard
const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now, repeatAfterHandler); //see method: fsrs.repeat()
const { card, log } = repeatFormAfterHandler[Rating.Hard];
const rollbackFromAfterHandler = f.rollback(card, log, cardAfterHandler);
See
https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45