Update FSRS

- up to 10x performance increase in optimal retention
- expose loss aversion
- use SpinBoxes
This commit is contained in:
Damien Elmes 2023-09-30 16:10:22 +10:00
parent 074f452762
commit 50c8a1ba9f
7 changed files with 64 additions and 198 deletions

108
Cargo.lock generated
View file

@ -573,7 +573,7 @@ checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]] [[package]]
name = "burn" name = "burn"
version = "0.10.0" version = "0.10.0"
source = "git+https://github.com/burn-rs/burn.git?rev=d7e9e750992229ed6a47101341f4630705fd564c#d7e9e750992229ed6a47101341f4630705fd564c" source = "git+https://github.com/burn-rs/burn.git?rev=d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c#d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c"
dependencies = [ dependencies = [
"burn-core", "burn-core",
"burn-train", "burn-train",
@ -582,7 +582,7 @@ dependencies = [
[[package]] [[package]]
name = "burn-autodiff" name = "burn-autodiff"
version = "0.10.0" version = "0.10.0"
source = "git+https://github.com/burn-rs/burn.git?rev=d7e9e750992229ed6a47101341f4630705fd564c#d7e9e750992229ed6a47101341f4630705fd564c" source = "git+https://github.com/burn-rs/burn.git?rev=d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c#d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c"
dependencies = [ dependencies = [
"burn-common", "burn-common",
"burn-tensor", "burn-tensor",
@ -594,7 +594,7 @@ dependencies = [
[[package]] [[package]]
name = "burn-common" name = "burn-common"
version = "0.10.0" version = "0.10.0"
source = "git+https://github.com/burn-rs/burn.git?rev=d7e9e750992229ed6a47101341f4630705fd564c#d7e9e750992229ed6a47101341f4630705fd564c" source = "git+https://github.com/burn-rs/burn.git?rev=d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c#d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c"
dependencies = [ dependencies = [
"const-random", "const-random",
"rand 0.8.5", "rand 0.8.5",
@ -605,7 +605,7 @@ dependencies = [
[[package]] [[package]]
name = "burn-core" name = "burn-core"
version = "0.10.0" version = "0.10.0"
source = "git+https://github.com/burn-rs/burn.git?rev=d7e9e750992229ed6a47101341f4630705fd564c#d7e9e750992229ed6a47101341f4630705fd564c" source = "git+https://github.com/burn-rs/burn.git?rev=d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c#d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c"
dependencies = [ dependencies = [
"bincode", "bincode",
"burn-autodiff", "burn-autodiff",
@ -630,12 +630,11 @@ dependencies = [
[[package]] [[package]]
name = "burn-dataset" name = "burn-dataset"
version = "0.10.0" version = "0.10.0"
source = "git+https://github.com/burn-rs/burn.git?rev=d7e9e750992229ed6a47101341f4630705fd564c#d7e9e750992229ed6a47101341f4630705fd564c" source = "git+https://github.com/burn-rs/burn.git?rev=d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c#d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c"
dependencies = [ dependencies = [
"csv 1.2.2", "csv 1.2.2",
"derive-new", "derive-new",
"dirs", "dirs",
"gix-tempfile",
"rand 0.8.5", "rand 0.8.5",
"rmp-serde", "rmp-serde",
"sanitize-filename", "sanitize-filename",
@ -650,7 +649,7 @@ dependencies = [
[[package]] [[package]]
name = "burn-derive" name = "burn-derive"
version = "0.10.0" version = "0.10.0"
source = "git+https://github.com/burn-rs/burn.git?rev=d7e9e750992229ed6a47101341f4630705fd564c#d7e9e750992229ed6a47101341f4630705fd564c" source = "git+https://github.com/burn-rs/burn.git?rev=d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c#d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c"
dependencies = [ dependencies = [
"derive-new", "derive-new",
"proc-macro2", "proc-macro2",
@ -661,7 +660,7 @@ dependencies = [
[[package]] [[package]]
name = "burn-ndarray" name = "burn-ndarray"
version = "0.10.0" version = "0.10.0"
source = "git+https://github.com/burn-rs/burn.git?rev=d7e9e750992229ed6a47101341f4630705fd564c#d7e9e750992229ed6a47101341f4630705fd564c" source = "git+https://github.com/burn-rs/burn.git?rev=d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c#d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c"
dependencies = [ dependencies = [
"burn-autodiff", "burn-autodiff",
"burn-common", "burn-common",
@ -679,7 +678,7 @@ dependencies = [
[[package]] [[package]]
name = "burn-tensor" name = "burn-tensor"
version = "0.10.0" version = "0.10.0"
source = "git+https://github.com/burn-rs/burn.git?rev=d7e9e750992229ed6a47101341f4630705fd564c#d7e9e750992229ed6a47101341f4630705fd564c" source = "git+https://github.com/burn-rs/burn.git?rev=d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c#d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c"
dependencies = [ dependencies = [
"burn-tensor-testgen", "burn-tensor-testgen",
"derive-new", "derive-new",
@ -695,7 +694,7 @@ dependencies = [
[[package]] [[package]]
name = "burn-tensor-testgen" name = "burn-tensor-testgen"
version = "0.10.0" version = "0.10.0"
source = "git+https://github.com/burn-rs/burn.git?rev=d7e9e750992229ed6a47101341f4630705fd564c#d7e9e750992229ed6a47101341f4630705fd564c" source = "git+https://github.com/burn-rs/burn.git?rev=d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c#d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -705,7 +704,7 @@ dependencies = [
[[package]] [[package]]
name = "burn-train" name = "burn-train"
version = "0.10.0" version = "0.10.0"
source = "git+https://github.com/burn-rs/burn.git?rev=d7e9e750992229ed6a47101341f4630705fd564c#d7e9e750992229ed6a47101341f4630705fd564c" source = "git+https://github.com/burn-rs/burn.git?rev=d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c#d06cc2f239c53e7f88dad7e0b2bbe6757a17d66c"
dependencies = [ dependencies = [
"burn-core", "burn-core",
"derive-new", "derive-new",
@ -1128,19 +1127,6 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "dashmap"
version = "5.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
"cfg-if",
"hashbrown 0.14.0",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]] [[package]]
name = "data-encoding" name = "data-encoding"
version = "2.4.0" version = "2.4.0"
@ -1359,15 +1345,6 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "faster-hex"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "239f7bfb930f820ab16a9cd95afc26f88264cf6905c960b340a615384aa3338a"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "1.9.0" version = "1.9.0"
@ -1528,7 +1505,7 @@ dependencies = [
[[package]] [[package]]
name = "fsrs" name = "fsrs"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/open-spaced-repetition/fsrs-rs.git?rev=9e20a8b32ebae50230cc39e2331e9593660d56ed#9e20a8b32ebae50230cc39e2331e9593660d56ed" source = "git+https://github.com/open-spaced-repetition/fsrs-rs.git?rev=5d67e1c0c82e56b49e3c9b3403da4ce703823b5f#5d67e1c0c82e56b49e3c9b3403da4ce703823b5f"
dependencies = [ dependencies = [
"burn", "burn",
"itertools 0.11.0", "itertools 0.11.0",
@ -1536,6 +1513,7 @@ dependencies = [
"ndarray", "ndarray",
"ndarray-rand", "ndarray-rand",
"rand 0.8.5", "rand 0.8.5",
"rayon",
"serde", "serde",
"snafu", "snafu",
"strum 0.25.0", "strum 0.25.0",
@ -1726,58 +1704,6 @@ version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
[[package]]
name = "gix-features"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f77decb545f63a52852578ef5f66ecd71017ffc1983d551d5fa2328d6d9817f"
dependencies = [
"gix-hash",
"gix-trace",
"libc",
]
[[package]]
name = "gix-fs"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d5089f3338647776733a75a800a664ab046f56f21c515fa4722e395f877ef8"
dependencies = [
"gix-features",
]
[[package]]
name = "gix-hash"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d4796bac3aaf0c2f8bea152ca924ae3bdc5f135caefe6431116bcd67e98eab9"
dependencies = [
"faster-hex",
"thiserror",
]
[[package]]
name = "gix-tempfile"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea558d3daf3b1d0001052b12218c66c8f84788852791333b633d7eeb6999db1"
dependencies = [
"dashmap",
"gix-fs",
"libc",
"once_cell",
"parking_lot",
"signal-hook",
"signal-hook-registry",
"tempfile",
]
[[package]]
name = "gix-trace"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b6d623a1152c3facb79067d6e2ecdae48130030cf27d6eb21109f13bd7b836"
[[package]] [[package]]
name = "glob" name = "glob"
version = "0.3.1" version = "0.3.1"
@ -4121,16 +4047,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
[[package]]
name = "signal-hook"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.1" version = "1.4.1"

View file

@ -40,7 +40,7 @@ rev = "184b2ca50ed39ca43da13f0b830a463861adb9ca"
[workspace.dependencies.fsrs] [workspace.dependencies.fsrs]
git = "https://github.com/open-spaced-repetition/fsrs-rs.git" git = "https://github.com/open-spaced-repetition/fsrs-rs.git"
rev = "9e20a8b32ebae50230cc39e2331e9593660d56ed" rev = "5d67e1c0c82e56b49e3c9b3403da4ce703823b5f"
# path = "../../../fsrs-rs" # path = "../../../fsrs-rs"
[workspace.dependencies] [workspace.dependencies]

View file

@ -656,15 +656,6 @@
"license_file": null, "license_file": null,
"description": "Bare bones CSV parsing with no_std support." "description": "Bare bones CSV parsing with no_std support."
}, },
{
"name": "dashmap",
"version": "5.5.3",
"authors": "Acrimon <joel.wejdenstal@gmail.com>",
"repository": "https://github.com/xacrimon/dashmap",
"license": "MIT",
"license_file": null,
"description": "Blazing fast concurrent HashMap for Rust."
},
{ {
"name": "data-encoding", "name": "data-encoding",
"version": "2.4.0", "version": "2.4.0",
@ -845,15 +836,6 @@
"license_file": null, "license_file": null,
"description": "Fallible streaming iteration" "description": "Fallible streaming iteration"
}, },
{
"name": "faster-hex",
"version": "0.8.1",
"authors": "zhangsoledad <787953403@qq.com>",
"repository": "https://github.com/NervosFoundation/faster-hex",
"license": "MIT",
"license_file": null,
"description": "Fast hex encoding."
},
{ {
"name": "fastrand", "name": "fastrand",
"version": "1.9.0", "version": "1.9.0",
@ -1133,51 +1115,6 @@
"license_file": null, "license_file": null,
"description": "A library for reading and writing the DWARF debugging format." "description": "A library for reading and writing the DWARF debugging format."
}, },
{
"name": "gix-features",
"version": "0.33.0",
"authors": "Sebastian Thiel <sebastian.thiel@icloud.com>",
"repository": "https://github.com/Byron/gitoxide",
"license": "Apache-2.0 OR MIT",
"license_file": null,
"description": "A crate to integrate various capabilities using compile-time feature flags"
},
{
"name": "gix-fs",
"version": "0.5.0",
"authors": "Sebastian Thiel <sebastian.thiel@icloud.com>",
"repository": "https://github.com/Byron/gitoxide",
"license": "Apache-2.0 OR MIT",
"license_file": null,
"description": "A crate providing file system specific utilities to `gitoxide`"
},
{
"name": "gix-hash",
"version": "0.12.0",
"authors": "Sebastian Thiel <sebastian.thiel@icloud.com>",
"repository": "https://github.com/Byron/gitoxide",
"license": "Apache-2.0 OR MIT",
"license_file": null,
"description": "Borrowed and owned git hash digests used to identify git objects"
},
{
"name": "gix-tempfile",
"version": "8.0.0",
"authors": "Sebastian Thiel <sebastian.thiel@icloud.com>",
"repository": "https://github.com/Byron/gitoxide",
"license": "Apache-2.0 OR MIT",
"license_file": null,
"description": "A tempfile implementation with a global registry to assure cleanup"
},
{
"name": "gix-trace",
"version": "0.1.3",
"authors": "Sebastian Thiel <sebastian.thiel@icloud.com>",
"repository": "https://github.com/Byron/gitoxide",
"license": "Apache-2.0 OR MIT",
"license_file": null,
"description": "A crate to provide minimal `tracing` support that can be turned off to zero cost"
},
{ {
"name": "h2", "name": "h2",
"version": "0.3.21", "version": "0.3.21",
@ -2708,15 +2645,6 @@
"license_file": null, "license_file": null,
"description": "A lock-free concurrent slab." "description": "A lock-free concurrent slab."
}, },
{
"name": "signal-hook",
"version": "0.3.17",
"authors": "Michal 'vorner' Vaner <vorner@vorner.cz>|Thomas Himmelstoss <thimm@posteo.de>",
"repository": "https://github.com/vorner/signal-hook",
"license": "Apache-2.0 OR MIT",
"license_file": null,
"description": "Unix signal handling"
},
{ {
"name": "signal-hook-registry", "name": "signal-hook-registry",
"version": "1.4.1", "version": "1.4.1",

View file

@ -362,6 +362,7 @@ message ComputeOptimalRetentionRequest {
uint32 max_minutes_of_study_per_day = 4; uint32 max_minutes_of_study_per_day = 4;
uint32 max_interval = 5; uint32 max_interval = 5;
string search = 6; string search = 6;
double loss_aversion = 7;
} }
message ComputeOptimalRetentionResponse { message ComputeOptimalRetentionResponse {
@ -369,18 +370,18 @@ message ComputeOptimalRetentionResponse {
} }
message OptimalRetentionParameters { message OptimalRetentionParameters {
double recall_secs_hard = 6; double recall_secs_hard = 1;
double recall_secs_good = 7; double recall_secs_good = 2;
double recall_secs_easy = 8; double recall_secs_easy = 3;
double forget_secs = 9; double forget_secs = 4;
double learn_secs = 10; double learn_secs = 5;
double first_rating_probability_again = 11; double first_rating_probability_again = 6;
double first_rating_probability_hard = 12; double first_rating_probability_hard = 7;
double first_rating_probability_good = 13; double first_rating_probability_good = 8;
double first_rating_probability_easy = 14; double first_rating_probability_easy = 9;
double review_rating_probability_hard = 15; double review_rating_probability_hard = 10;
double review_rating_probability_good = 16; double review_rating_probability_good = 11;
double review_rating_probability_easy = 17; double review_rating_probability_easy = 12;
} }
message GetOptimalRetentionParametersRequest { message GetOptimalRetentionParametersRequest {

View file

@ -67,9 +67,9 @@ pub enum RevlogReviewKind {
Learning = 0, Learning = 0,
Review = 1, Review = 1,
Relearning = 2, Relearning = 2,
/// Old Anki versions called this "Cram" or "Early", and assigned it when /// Old Anki versions called this "Cram" or "Early". It's assigned when
/// reviewing cards ahead. It is now only used for filtered decks with /// reviewing cards before they're due, or when rescheduling is
/// rescheduling disabled. /// disabled.
Filtered = 3, Filtered = 3,
Manual = 4, Manual = 4,
} }

View file

@ -49,6 +49,7 @@ impl Collection {
p.review_rating_probability_good, p.review_rating_probability_good,
p.review_rating_probability_easy, p.review_rating_probability_easy,
], ],
loss_aversion: req.loss_aversion,
}, },
&req.weights, &req.weights,
|ip| { |ip| {

View file

@ -20,6 +20,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import SettingTitle from "../components/SettingTitle.svelte"; import SettingTitle from "../components/SettingTitle.svelte";
import type { DeckOptionsState } from "./lib"; import type { DeckOptionsState } from "./lib";
import SpinBoxFloatRow from "./SpinBoxFloatRow.svelte"; import SpinBoxFloatRow from "./SpinBoxFloatRow.svelte";
import SpinBoxRow from "./SpinBoxRow.svelte";
import WeightsInputRow from "./WeightsInputRow.svelte"; import WeightsInputRow from "./WeightsInputRow.svelte";
export let state: DeckOptionsState; export let state: DeckOptionsState;
@ -49,6 +50,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
deckSize: 10000, deckSize: 10000,
daysToSimulate: 365, daysToSimulate: 365,
maxMinutesOfStudyPerDay: 30, maxMinutesOfStudyPerDay: 30,
lossAversion: 2.5,
}); });
$: if (optimalRetentionRequest.daysToSimulate > 3650) { $: if (optimalRetentionRequest.daysToSimulate > 3650) {
optimalRetentionRequest.daysToSimulate = 3650; optimalRetentionRequest.daysToSimulate = 3650;
@ -253,23 +255,41 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<details> <details>
<summary>{tr.deckConfigComputeOptimalRetention()} (experimental)</summary> <summary>{tr.deckConfigComputeOptimalRetention()} (experimental)</summary>
Deck size: <SpinBoxRow
<br /> bind:value={optimalRetentionRequest.deckSize}
<input type="number" bind:value={optimalRetentionRequest.deckSize} /> defaultValue={10000}
<br /> min={100}
max={999999}
>
<SettingTitle>Deck size</SettingTitle>
</SpinBoxRow>
Days to simulate <SpinBoxRow
<br /> bind:value={optimalRetentionRequest.daysToSimulate}
<input type="number" bind:value={optimalRetentionRequest.daysToSimulate} /> defaultValue={365}
<br /> min={1}
max={3650}
>
<SettingTitle>Days to simulate</SettingTitle>
</SpinBoxRow>
Target minutes of study per day: <SpinBoxRow
<br />
<input
type="number"
bind:value={optimalRetentionRequest.maxMinutesOfStudyPerDay} bind:value={optimalRetentionRequest.maxMinutesOfStudyPerDay}
/> defaultValue={30}
<br /> min={1}
max={1800}
>
<SettingTitle>Minutes study/day</SettingTitle>
</SpinBoxRow>
<SpinBoxFloatRow
bind:value={optimalRetentionRequest.lossAversion}
defaultValue={2.5}
min={1.0}
max={3.0}
>
<SettingTitle>Loss aversion</SettingTitle>
</SpinBoxFloatRow>
<button <button
class="btn {computingRetention ? 'btn-warning' : 'btn-primary'}" class="btn {computingRetention ? 'btn-warning' : 'btn-primary'}"