refactor optimal retention to minimize workload per memorization & fix progress goes backwards (#3065)

* Feat/optimal retention for minimal workload per memorization

* ./ninja fix:minilints

* update to FSRS-rs 0.5.2

* update to FSRS-rs 0.5.3

* ./ninja fix:minilints

* 'estimated retention' -> 'predicted optimal retention'; add warning (dae)
This commit is contained in:
Jarrett Ye 2024-03-11 17:16:37 +08:00 committed by GitHub
parent 04fe3655e2
commit 8c9d7d64d9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 22 additions and 35 deletions

4
Cargo.lock generated
View file

@ -1792,9 +1792,9 @@ dependencies = [
[[package]]
name = "fsrs"
version = "0.5.0"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c7e6a1986cc2b7a64445d84e2c453ecd8d95dcf90b797205c54573697e10b17"
checksum = "50f8d2ba5394c6e36fa01d88df181206bcae8b7a4ef3c5653eb4d635e3f595e4"
dependencies = [
"burn",
"itertools 0.12.1",

View file

@ -35,7 +35,7 @@ git = "https://github.com/ankitects/linkcheck.git"
rev = "184b2ca50ed39ca43da13f0b830a463861adb9ca"
[workspace.dependencies.fsrs]
version = "0.5.0"
version = "0.5.3"
# git = "https://github.com/open-spaced-repetition/fsrs-rs.git"
# rev = "58ca25ed2bc4bb1dc376208bbcaed7f5a501b941"
# path = "../open-spaced-repetition/fsrs-rs"

View file

@ -1198,7 +1198,7 @@
},
{
"name": "fsrs",
"version": "0.5.0",
"version": "0.5.3",
"authors": "Open Spaced Repetition",
"repository": "https://github.com/open-spaced-repetition/fsrs-rs",
"license": "BSD-3-Clause",

View file

@ -357,7 +357,7 @@ deck-config-get-params = Get Params
deck-config-fsrs-on-all-clients =
Please ensure all of your Anki clients are Anki(Mobile) 23.10+ or AnkiDroid 2.17+. FSRS will
not work correctly if one of your clients is older.
deck-config-estimated-retention = Estimated retention: { $num }
deck-config-predicted-optimal-retention = Predicted optimal retention: { $num }
deck-config-complete = { $num }% complete.
deck-config-iterations = Iteration: { $count }...
deck-config-reschedule-cards-on-change = Reschedule cards on change

View file

@ -373,12 +373,10 @@ message FsrsReview {
message ComputeOptimalRetentionRequest {
repeated float weights = 1;
uint32 deck_size = 2;
uint32 days_to_simulate = 3;
uint32 max_minutes_of_study_per_day = 4;
uint32 max_interval = 5;
string search = 6;
double loss_aversion = 7;
uint32 days_to_simulate = 2;
uint32 max_interval = 3;
string search = 4;
double loss_aversion = 5;
}
message ComputeOptimalRetentionResponse {

View file

@ -28,12 +28,15 @@ impl Collection {
invalid_input!("no days to simulate")
}
let p = self.get_optimal_retention_parameters(&req.search)?;
let learn_span = req.days_to_simulate as usize;
let learn_limit = 10;
let deck_size = learn_span * learn_limit;
Ok(fsrs
.optimal_retention(
&SimulatorConfig {
deck_size: req.deck_size as usize,
deck_size,
learn_span: req.days_to_simulate as usize,
max_cost_perday: req.max_minutes_of_study_per_day as f64 * 60.0,
max_cost_perday: f64::MAX,
max_ivl: req.max_interval as f64,
recall_costs: [p.recall_secs_hard, p.recall_secs_good, p.recall_secs_easy],
forget_cost: p.forget_secs,
@ -50,7 +53,7 @@ impl Collection {
p.review_rating_probability_easy,
],
loss_aversion: req.loss_aversion,
learn_limit: usize::MAX,
learn_limit,
review_limit: usize::MAX,
},
&req.weights,

View file

@ -55,9 +55,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
| undefined;
const optimalRetentionRequest = new ComputeOptimalRetentionRequest({
deckSize: 10000,
daysToSimulate: 365,
maxMinutesOfStudyPerDay: 30,
lossAversion: 2.5,
});
$: if (optimalRetentionRequest.daysToSimulate > 3650) {
@ -249,7 +247,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
if (!retention) {
return "";
}
return tr.deckConfigEstimatedRetention({ num: retention.toFixed(2) });
return tr.deckConfigPredictedOptimalRetention({ num: retention.toFixed(2) });
}
</script>
@ -348,15 +346,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<details>
<summary>{tr.deckConfigComputeOptimalRetention()} (experimental)</summary>
<SpinBoxRow
bind:value={optimalRetentionRequest.deckSize}
defaultValue={10000}
min={100}
max={99999}
>
<SettingTitle>Deck size</SettingTitle>
</SpinBoxRow>
<SpinBoxRow
bind:value={optimalRetentionRequest.daysToSimulate}
defaultValue={365}
@ -366,15 +355,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<SettingTitle>Days to simulate</SettingTitle>
</SpinBoxRow>
<SpinBoxRow
bind:value={optimalRetentionRequest.maxMinutesOfStudyPerDay}
defaultValue={30}
min={1}
max={1800}
>
<SettingTitle>Minutes study/day</SettingTitle>
</SpinBoxRow>
<button
class="btn {computingRetention ? 'btn-warning' : 'btn-primary'}"
disabled={!computingRetention && computing}
@ -389,6 +369,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{#if optimalRetention}
{estimatedRetention(optimalRetention)}
{#if optimalRetention > $config.desiredRetention}
<Warning
warning="Your desired retention is below optimal. Increasing it is recommended."
className="alert-warning"
/>
{/if}
{/if}
<div>{computeRetentionProgressString}</div>
</details>