Skip to content

Commit

Permalink
Fix/set mean reversion target to d0(4) (#103)
Browse files Browse the repository at this point in the history
* Set mean reversion target to D0(4)

open-spaced-repetition/fsrs-optimizer#124

* test/update test

* update default parameters
  • Loading branch information
ishiko732 authored Jul 27, 2024
1 parent 9285e33 commit 231539e
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 55 deletions.
6 changes: 4 additions & 2 deletions __tests__/FSRSV5.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ describe('FSRS V5 ', () => {
scheduling_cards = f.repeat(card, now)
}
expect(ivl_history).toEqual([
0, 4, 17, 57, 163, 412, 0, 0, 8, 15, 27, 49, 86,
0, 4, 17, 62, 198, 563, 0, 0, 9, 27, 74, 190, 457,
])
})

Expand Down Expand Up @@ -135,6 +135,8 @@ describe('fsrs.next method', () => {
const card = createEmptyCard()
const now = new Date()
const g = Rating.Manual as unknown as Grade
expect(() => fsrs.next(card, now, g)).toThrow('Cannot review a manual rating')
expect(() => fsrs.next(card, now, g)).toThrow(
'Cannot review a manual rating'
)
})
})
17 changes: 12 additions & 5 deletions __tests__/algorithm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,17 @@ describe('next_ds', () => {
return Math.min(Math.max(+new Decimal(difficulty).toFixed(8), 1), 10)
}

function init_difficulty(g: number) {
return +new Decimal(params.w[4])
.sub(new Decimal(params.w[5]).mul(new Decimal(g).sub(1)).exp())
.add(1)
.toFixed(8)
}

const next_d = new Decimal(d)
.sub(new Decimal(params.w[6]).mul(new Decimal(g - 3)))
.toNumber()
return constrain_difficulty(mean_reversion(params.w[4], next_d))
return constrain_difficulty(mean_reversion(init_difficulty(4), next_d))
}

const collection: number[] = []
Expand All @@ -131,7 +138,7 @@ describe('next_ds', () => {
collection.push(d)
expected.push(expected_d)
})
expect(collection).toEqual([7.0109708, 6.07771798, 5.14446516, 4.21121234])
expect(collection).toEqual([7.04017216, 5.9999955, 4.95981884, 3.91964218])
expect(collection).toEqual(expected)
})

Expand Down Expand Up @@ -238,15 +245,15 @@ describe('next_ds', () => {
expected_next_s.push(next_s(d[index], s[index], r[index], grade))
})
expect(s_recall_collection).toEqual([
28.60303182, 16.24044223, 68.61087855, 237.0869306,
27.43740902, 15.27687386, 65.24019626, 224.35058851,
])
expect(s_recall_collection).toEqual(expected_s_recall)
expect(s_fail_collection).toEqual([
1.79896722, 2.0890141, 2.48973974, 2.99909845,
1.73909651, 2.0293769, 2.43393181, 2.95208552,
])
expect(s_fail_collection).toEqual(expected_s_fail)
expect(s_short_collection).toEqual([
2.57947991, 4.20673912, 6.86055123, 11.18851485,
2.54268521, 4.20645686, 6.95889497, 11.51235369,
])
expect(s_short_collection).toEqual(expected_s_short)

Expand Down
6 changes: 3 additions & 3 deletions __tests__/default.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {

describe('default params', () => {
const expected_w = [
0.4197, 1.1869, 3.0412, 15.2441, 7.1434, 0.6477, 1.0007, 0.0674, 1.6597,
0.1712, 1.1178, 2.0225, 0.0904, 0.3025, 2.1214, 0.2498, 2.9466, 0.4891,
0.6468,
0.4072, 1.1829, 3.1262, 15.4722, 7.2102, 0.5316, 1.0651, 0.0234, 1.616,
0.1544, 1.0824, 1.9813, 0.0953, 0.2975, 2.2042, 0.2407, 2.9466, 0.5034,
0.6567,
]
expect(default_request_retention).toEqual(0.9)
expect(default_maximum_interval).toEqual(36500)
Expand Down
72 changes: 42 additions & 30 deletions __tests__/impl/long-term_schduler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,18 @@ describe('Long-term schduler', () => {
now = card.due
}

expect(ivl_history).toEqual([3, 6, 17, 42, 95, 200, 8, 2, 3, 5, 8, 14, 23])
expect(ivl_history).toEqual([
3, 7, 21, 60, 158, 390, 11, 2, 5, 13, 33, 79, 184,
])
expect(s_history).toEqual([
0.57587467, 6.28341418, 16.83356103, 41.95128557, 95.07063986,
199.53765138, 8.31519008, 1.96276113, 3.06877302, 4.90880017, 8.15177579,
13.50873393, 22.92901865,
0.57587467, 6.87994283, 21.35900027, 59.97135827, 158.31738168,
390.28403347, 10.97158339, 2.35722772, 5.41695678, 13.11380293,
32.50206204, 79.19030952, 184.25083571,
])
expect(d_history).toEqual([
7.1434, 7.1434, 7.1434, 7.1434, 7.1434, 7.1434, 9.00990564, 10,
9.80746516, 9.62790717, 9.46045139, 9.30428213, 9.15863867,
6.74032397, 6.36441526, 6.0138428, 5.68689892, 5.38199106, 5.09763399,
6.69894823, 8.19233389, 7.71855971, 7.27671791, 6.86465625, 6.48036755,
6.1219799,
])
})

Expand Down Expand Up @@ -85,15 +88,15 @@ describe('Long-term schduler', () => {
d_history.push(card.difficulty)
now = card.due
}
expect(ivl_history).toEqual([1, 2, 3, 8, 2, 2, 4, 10])
expect(ivl_history).toEqual([1, 2, 3, 14, 3, 4, 8, 34])

expect(s_history).toEqual([
0.21652154, 0.51780862, 1.8183783, 8.18593986, 1.96087115, 2.23717242,
3.33185406, 10.31008873,
0.21652154, 0.57883165, 2.75860692, 14.17790785, 2.66869241, 3.62352774,
7.6088889, 33.82907094,
])
expect(d_history).toEqual([
9.00990564, 9.81735598, 9.63713135, 8.53580104, 10, 10, 9.80746516,
8.69465435,
8.60682961, 9.03837124, 8.50757415, 7.07929996, 8.54704991, 8.98262069,
8.45558118, 7.03081132,
])
})

Expand Down Expand Up @@ -121,15 +124,15 @@ describe('Long-term schduler', () => {
d_history.push(card.difficulty)
now = card.due
}
expect(ivl_history).toEqual([2, 3, 17, 3, 4, 6, 18, 3])
expect(ivl_history).toEqual([2, 4, 26, 4, 5, 13, 67, 5])

expect(s_history).toEqual([
0.35311368, 3.40179546, 16.86596974, 2.9223039, 3.73601023, 6.27595217,
18.057595, 2.96541083,
0.35311368, 3.82215283, 25.65963413, 3.62919153, 5.38579333, 12.54261943,
66.53407633, 5.47466879,
])
expect(d_history).toEqual([
8.07665282, 8.01375158, 7.02183706, 8.89653604, 9.71162749, 9.53852896,
8.44384445, 10,
7.67357679, 7.23476684, 5.89227986, 7.44003496, 7.95021855, 7.49276295,
6.13288703, 7.66442521,
])
})

Expand Down Expand Up @@ -158,15 +161,15 @@ describe('Long-term schduler', () => {
now = card.due
}

expect(ivl_history).toEqual([3, 17, 3, 4, 8, 30, 4, 4])
expect(ivl_history).toEqual([3, 19, 3, 5, 13, 73, 6, 8])

expect(s_history).toEqual([
0.57587467, 17.3937106, 2.98322161, 4.08808234, 7.99405969, 29.76078201,
3.78204811, 4.44627015,
0.57587467, 19.1514419, 3.17243454, 4.70631523, 12.88705197, 72.51830855,
5.70211402, 8.22198803,
])
expect(d_history).toEqual([
7.1434, 6.21014718, 8.13955406, 9.0056661, 8.88014936, 7.82983963,
9.65007924, 10,
6.74032397, 5.43116244, 7.00999686, 7.54916502, 7.11874042, 5.78407362,
7.33912183, 7.85610697,
])
})
test('test5', () => {
Expand Down Expand Up @@ -194,15 +197,15 @@ describe('Long-term schduler', () => {
now = card.due
}

expect(ivl_history).toEqual([4, 1, 2, 3, 13, 3, 3, 5])
expect(ivl_history).toEqual([4, 1, 2, 5, 30, 4, 6, 14])

expect(s_history).toEqual([
0.93916394, 0.70772253, 1.16119435, 3.42301186, 12.97577367, 2.53486926,
3.06202367, 4.60523893,
0.93916394, 0.71202904, 1.28884718, 4.82236231, 29.76149698, 3.87021442,
5.53831177, 13.53596018,
])
expect(d_history).toEqual([
6.21014718, 8.13955406, 9.0056661, 8.88014936, 7.82983963, 9.65007924, 10,
9.80746516,
5.80707115, 7.36056932, 7.8761089, 7.42364829, 6.0684307, 7.60431324,
8.10342447, 7.63564279,
])
})

Expand Down Expand Up @@ -238,11 +241,20 @@ describe('Long-term schduler', () => {
d_history.push(card.difficulty)
state_history.push(State[card.state])
}
expect(ivl_history).toEqual([0, 4, 1, 4, 12, 0])
expect(ivl_history).toEqual([0, 4, 1, 4, 15, 0])
expect(s_history).toEqual([
3.0412, 3.0412, 1.20788692, 3.83856852, 12.23542321, 2.48288917,
3.0412, 3.0412, 1.21778427, 4.32308454, 14.84659978, 2.81505627,
])
expect(d_history).toEqual([
4.49094334, 4.26664289, 5.92396593, 5.60307975, 5.3038213, 6.89123851,
])
expect(state_history).toEqual([
'Learning',
'Review',
'Review',
'Review',
'Review',
'Relearning',
])
expect(d_history).toEqual([4.49094334, 4.66971892, 6.70295066, 6.73263695,6.76032238,8.65264745])
expect(state_history).toEqual(['Learning', 'Review', 'Review', 'Review','Review','Relearning'])
})
})
14 changes: 7 additions & 7 deletions debug/short-term.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,22 @@ function test1() {

assert.deepStrictEqual(
ivl_history,
[0, 4, 17, 57, 163, 412, 0, 0, 8, 15, 27, 49, 86]
[0, 4, 15, 48, 136, 351, 0, 0, 7, 13, 24, 43, 77]
)
assert.deepStrictEqual(
s_history,
[
3.0412, 4.17286168, 16.55123695, 56.74378762, 163.35708827, 411.77071586,
11.15423471, 5.75442486, 7.89570531, 14.90748589, 27.437534, 48.90521597,
85.82782993,
3.1262, 4.35097949, 14.94870008, 47.78790366, 136.29251941, 351.35118365,
9.99090459, 5.08074507, 7.07127426, 13.09253023, 23.92805755, 43.23645988,
76.59611345,
]
)
assert.deepStrictEqual(
d_history,
[
4.49094334, 4.66971892, 4.83644502, 4.99193379, 5.13694261, 5.27217784,
7.26480385, 9.12312687, 8.98969328, 8.86525311, 8.74920021, 8.64096928,
8.54003311,
5.31457783, 5.26703555, 5.22060576, 5.17526243, 5.13098013, 5.08773404,
7.12585323, 9.11628043, 8.97977831, 8.84647034, 8.71628178, 8.58913963,
8.4649726,
]
)
}
Expand Down
6 changes: 3 additions & 3 deletions src/fsrs/algorithm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,16 +165,16 @@ export class FSRSAlgorithm {

/**
* The formula used is :
* $$\text{next}_d = D - w_6 \cdot (R - 2)$$
* $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot \text{next}_d$$
* $$\text{next}_d = D - w_6 \cdot (g - 3)$$
* $$D^\prime(D,R) = w_7 \cdot D_0(4) +(1 - w_7) \cdot \text{next}_d$$
* @param {number} d Difficulty $$D \in [1,10]$$
* @param {Grade} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
* @return {number} $$\text{next}_D$$
*/
next_difficulty(d: number, g: Grade): number {
const next_d = d - this.param.w[6] * (g - 3)
return this.constrain_difficulty(
this.mean_reversion(this.param.w[4], next_d)
this.mean_reversion(this.init_difficulty(Rating.Easy), next_d)
)
}

Expand Down
9 changes: 4 additions & 5 deletions src/fsrs/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { TypeConvert } from './convert'
export const default_request_retention = 0.9
export const default_maximum_interval = 36500
export const default_w = [
0.4197, 1.1869, 3.0412, 15.2441, 7.1434, 0.6477, 1.0007, 0.0674, 1.6597,
0.1712, 1.1178, 2.0225, 0.0904, 0.3025, 2.1214, 0.2498, 2.9466, 0.4891,
0.6468,
0.4072, 1.1829, 3.1262, 15.4722, 7.2102, 0.5316, 1.0651, 0.0234, 1.616,
0.1544, 1.0824, 1.9813, 0.0953, 0.2975, 2.2042, 0.2407, 2.9466, 0.5034,
0.6567,
]
export const default_enable_fuzz = false
export const defualt_enable_short_term = true
Expand All @@ -30,8 +30,7 @@ export const generatorParameters = (
maximum_interval: props?.maximum_interval || default_maximum_interval,
w: w,
enable_fuzz: props?.enable_fuzz ?? default_enable_fuzz,
enable_short_term:
props?.enable_short_term ?? defualt_enable_short_term,
enable_short_term: props?.enable_short_term ?? defualt_enable_short_term,
} satisfies FSRSParameters
}

Expand Down

0 comments on commit 231539e

Please sign in to comment.