Skip to content

Commit dd066ff

Browse files
authored
Update to FSRS-5 (#664)
1 parent fab78ae commit dd066ff

3 files changed

Lines changed: 540 additions & 399 deletions

File tree

fsrs4anki_optimizer.ipynb

Lines changed: 502 additions & 387 deletions
Large diffs are not rendered by default.

fsrs4anki_scheduler.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// FSRS4Anki v4.11.1 Scheduler Qt6
1+
// FSRS4Anki v5.0.0 Scheduler Qt6
22
set_version();
33
// The latest version will be released on https://github.com/open-spaced-repetition/fsrs4anki/releases/latest
44

@@ -8,7 +8,7 @@ const deckParams = [
88
{
99
// Default parameters of FSRS4Anki for global
1010
"deckName": "global config for FSRS4Anki",
11-
"w": [0.4, 0.6, 2.4, 5.8, 4.93, 0.94, 0.86, 0.01, 1.49, 0.14, 0.94, 2.18, 0.05, 0.34, 1.26, 0.29, 2.61],
11+
"w": [0.41, 1.18, 3.04, 15.24, 7.14, 0.64, 1.00, 0.06, 1.65, 0.17, 1.11, 2.02, 0.09, 0.30, 2.12, 0.24, 2.94, 0.48, 0.64],
1212
// The above parameters can be optimized via FSRS4Anki optimizer.
1313
// For details about the parameters, please see: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm
1414
// User's custom parameters for global
@@ -20,15 +20,15 @@ const deckParams = [
2020
{
2121
// Example 1: User's custom parameters for this deck and its sub-decks.
2222
"deckName": "MainDeck1",
23-
"w": [0.6, 0.9, 2.9, 6.8, 4.72, 1.02, 1, 0.04, 1.49, 0.17, 1.02, 2.15, 0.07, 0.35, 1.17, 0.32, 2.53],
23+
"w": [0.41, 1.18, 3.04, 15.24, 7.14, 0.64, 1.00, 0.06, 1.65, 0.17, 1.11, 2.02, 0.09, 0.30, 2.12, 0.24, 2.94, 0.48, 0.64],
2424
"requestRetention": 0.9,
2525
"maximumInterval": 36500,
2626
},
2727
{
2828
// Example 2: User's custom parameters for this deck and its sub-decks.
2929
// Don't omit any keys.
3030
"deckName": "MainDeck2::SubDeck::SubSubDeck",
31-
"w": [0.6, 0.9, 2.9, 6.8, 4.72, 1.02, 1, 0.04, 1.49, 0.17, 1.02, 2.15, 0.07, 0.35, 1.17, 0.32, 2.53],
31+
"w": [0.41, 1.18, 3.04, 15.24, 7.14, 0.64, 1.00, 0.06, 1.65, 0.17, 1.11, 2.02, 0.09, 0.30, 2.12, 0.24, 2.94, 0.48, 0.64],
3232
"requestRetention": 0.9,
3333
"maximumInterval": 36500,
3434
}
@@ -121,6 +121,16 @@ if (is_new()) {
121121
if (is_empty()) {
122122
init_states();
123123
}
124+
const last_d = customData.again.d;
125+
const last_s = customData.again.s;
126+
customData.again.d = next_difficulty(last_d, "again");
127+
customData.again.s = next_short_term_stability(last_s, ratings["again"]);
128+
customData.hard.d = next_difficulty(last_d, "hard");
129+
customData.hard.s = next_short_term_stability(last_s, ratings["hard"]);
130+
customData.good.d = next_difficulty(last_d, "good");
131+
customData.good.s = next_short_term_stability(last_s, ratings["good"]);
132+
customData.easy.d = next_difficulty(last_d, "easy");
133+
customData.easy.s = next_short_term_stability(last_s, ratings["easy"]);
124134
const good_interval = next_interval(customData.good.s);
125135
const easy_interval = Math.max(next_interval(customData.easy.s), good_interval + 1);
126136
if (states.good.normal?.review) {
@@ -212,6 +222,9 @@ function next_forget_stability(d, s, r) {
212222
(Math.pow(s + 1, w[13]) - 1) *
213223
Math.exp((1 - r) * w[14]), s).toFixed(2);
214224
}
225+
function next_short_term_stability(s, rating) {
226+
return +(s * Math.exp(w[17] * (rating - 3 + w[18]))).toFixed(2);
227+
}
215228
function init_states() {
216229
customData.again.d = init_difficulty("again");
217230
customData.again.s = init_stability("again");
@@ -223,7 +236,7 @@ function init_states() {
223236
customData.easy.s = init_stability("easy");
224237
}
225238
function init_difficulty(rating) {
226-
return +constrain_difficulty(w[4] - w[5] * (ratings[rating] - 3)).toFixed(2);
239+
return +constrain_difficulty(w[4] - Math.exp(w[5] * (ratings[rating] - 1)) + 1).toFixed(2);
227240
}
228241
function init_stability(rating) {
229242
return +Math.max(w[ratings[rating] - 1], 0.1).toFixed(2);
@@ -295,7 +308,7 @@ function is_empty() {
295308
return !customData.again.d | !customData.again.s | !customData.hard.d | !customData.hard.s | !customData.good.d | !customData.good.s | !customData.easy.d | !customData.easy.s;
296309
}
297310
function set_version() {
298-
const version = "v4.11.1";
311+
const version = "v5.0.0";
299312
customData.again.v = version;
300313
customData.hard.v = version;
301314
customData.good.v = version;

fsrs4anki_scheduler_qt5.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// FSRS4Anki v4.11.1 Scheduler Qt5
1+
// FSRS4Anki v5.0.0 Scheduler Qt5
22
set_version();
33
// The latest version will be released on https://github.com/open-spaced-repetition/fsrs4anki/releases/latest
44

@@ -8,7 +8,7 @@ const deckParams = [
88
{
99
// Default parameters of FSRS4Anki for global
1010
"deckName": "global config for FSRS4Anki",
11-
"w": [0.4, 0.6, 2.4, 5.8, 4.93, 0.94, 0.86, 0.01, 1.49, 0.14, 0.94, 2.18, 0.05, 0.34, 1.26, 0.29, 2.61],
11+
"w": [0.41, 1.18, 3.04, 15.24, 7.14, 0.64, 1.00, 0.06, 1.65, 0.17, 1.11, 2.02, 0.09, 0.30, 2.12, 0.24, 2.94, 0.48, 0.64],
1212
// The above parameters can be optimized via FSRS4Anki optimizer.
1313
// For details about the parameters, please see: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm
1414
// User's custom parameters for global
@@ -20,15 +20,15 @@ const deckParams = [
2020
{
2121
// Example 1: User's custom parameters for this deck and its sub-decks.
2222
"deckName": "MainDeck1",
23-
"w": [0.6, 0.9, 2.9, 6.8, 4.72, 1.02, 1, 0.04, 1.49, 0.17, 1.02, 2.15, 0.07, 0.35, 1.17, 0.32, 2.53],
23+
"w": [0.41, 1.18, 3.04, 15.24, 7.14, 0.64, 1.00, 0.06, 1.65, 0.17, 1.11, 2.02, 0.09, 0.30, 2.12, 0.24, 2.94, 0.48, 0.64],
2424
"requestRetention": 0.9,
2525
"maximumInterval": 36500,
2626
},
2727
{
2828
// Example 2: User's custom parameters for this deck and its sub-decks.
2929
// Don't omit any keys.
3030
"deckName": "MainDeck2::SubDeck::SubSubDeck",
31-
"w": [0.6, 0.9, 2.9, 6.8, 4.72, 1.02, 1, 0.04, 1.49, 0.17, 1.02, 2.15, 0.07, 0.35, 1.17, 0.32, 2.53],
31+
"w": [0.41, 1.18, 3.04, 15.24, 7.14, 0.64, 1.00, 0.06, 1.65, 0.17, 1.11, 2.02, 0.09, 0.30, 2.12, 0.24, 2.94, 0.48, 0.64],
3232
"requestRetention": 0.9,
3333
"maximumInterval": 36500,
3434
}
@@ -125,6 +125,16 @@ if (is_new()) {
125125
if (is_empty()) {
126126
init_states();
127127
}
128+
const last_d = customData.again.d;
129+
const last_s = customData.again.s;
130+
customData.again.d = next_difficulty(last_d, "again");
131+
customData.again.s = next_short_term_stability(last_s, ratings["again"]);
132+
customData.hard.d = next_difficulty(last_d, "hard");
133+
customData.hard.s = next_short_term_stability(last_s, ratings["hard"]);
134+
customData.good.d = next_difficulty(last_d, "good");
135+
customData.good.s = next_short_term_stability(last_s, ratings["good"]);
136+
customData.easy.d = next_difficulty(last_d, "easy");
137+
customData.easy.s = next_short_term_stability(last_s, ratings["easy"]);
128138
const good_interval = next_interval(customData.good.s);
129139
const easy_interval = Math.max(next_interval(customData.easy.s), good_interval + 1);
130140
if ((_states$good$normal2 = states.good.normal) !== null && _states$good$normal2 !== void 0 && _states$good$normal2.review) {
@@ -210,6 +220,9 @@ function next_recall_stability(d, s, r, rating) {
210220
function next_forget_stability(d, s, r) {
211221
return +Math.min(w[11] * Math.pow(d, -w[12]) * (Math.pow(s + 1, w[13]) - 1) * Math.exp((1 - r) * w[14]), s).toFixed(2);
212222
}
223+
function next_short_term_stability(s, rating) {
224+
return +(s * Math.exp(w[17] * (rating - 3 + w[18]))).toFixed(2);
225+
}
213226
function init_states() {
214227
customData.again.d = init_difficulty("again");
215228
customData.again.s = init_stability("again");
@@ -221,7 +234,7 @@ function init_states() {
221234
customData.easy.s = init_stability("easy");
222235
}
223236
function init_difficulty(rating) {
224-
return +constrain_difficulty(w[4] - w[5] * (ratings[rating] - 3)).toFixed(2);
237+
return +constrain_difficulty(w[4] - Math.exp(w[5] * (ratings[rating] - 1)) + 1).toFixed(2);
225238
}
226239
function init_stability(rating) {
227240
return +Math.max(w[ratings[rating] - 1], 0.1).toFixed(2);
@@ -304,7 +317,7 @@ function is_empty() {
304317
return !customData.again.d | !customData.again.s | !customData.hard.d | !customData.hard.s | !customData.good.d | !customData.good.s | !customData.easy.d | !customData.easy.s;
305318
}
306319
function set_version() {
307-
const version = "v4.11.1";
320+
const version = "v5.0.0";
308321
customData.again.v = version;
309322
customData.hard.v = version;
310323
customData.good.v = version;

0 commit comments

Comments
 (0)