From 0e0e3b923890103a24bd92dbdfbc1ffb40163993 Mon Sep 17 00:00:00 2001 From: Luc Mcgrady Date: Thu, 19 Jun 2025 23:56:03 +0100 Subject: [PATCH] Added: Loss aversion --- proto/anki/scheduler.proto | 3 ++- rslib/src/scheduler/fsrs/retention.rs | 14 +++++++++++--- ts/routes/deck-options/choices.ts | 4 ++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/proto/anki/scheduler.proto b/proto/anki/scheduler.proto index 2db0b937e..9d1bbdf37 100644 --- a/proto/anki/scheduler.proto +++ b/proto/anki/scheduler.proto @@ -391,7 +391,8 @@ message FsrsReview { enum CMRRTarget { memorized = 0; - stability = 1; + loss_aversion = 1; + stability = 2; }; message SimulateFsrsReviewRequest { diff --git a/rslib/src/scheduler/fsrs/retention.rs b/rslib/src/scheduler/fsrs/retention.rs index d6bdf22bf..b16fa6578 100644 --- a/rslib/src/scheduler/fsrs/retention.rs +++ b/rslib/src/scheduler/fsrs/retention.rs @@ -25,7 +25,7 @@ impl Collection { }; } - let target = req + let target_type = req .target .map(TryInto::try_into) .transpose() @@ -33,8 +33,9 @@ impl Collection { let days_to_simulate = req.days_to_simulate as f32; - let target = match target { + let target = match target_type { Some(CmrrTarget::Memorized) => None, + Some(CmrrTarget::LossAversion) => None, Some(CmrrTarget::Stability) => { wrap!(move |SimulationResult { cards, @@ -57,7 +58,14 @@ impl Collection { if req.days_to_simulate == 0 { invalid_input!("no days to simulate") } - let (config, cards) = self.simulate_request_to_config(&req)?; + let (mut config, cards) = self.simulate_request_to_config(&req)?; + + if target_type == Some(CmrrTarget::LossAversion) { + config.relearning_step_transitions[0][0] *= 2.; + config.relearning_step_transitions[1][0] *= 2.; + config.relearning_step_transitions[2][0] *= 2.; + } + Ok(fsrs .optimal_retention( &config, diff --git a/ts/routes/deck-options/choices.ts b/ts/routes/deck-options/choices.ts index 380906b44..c4ec7907d 100644 --- a/ts/routes/deck-options/choices.ts +++ b/ts/routes/deck-options/choices.ts @@ -206,6 +206,10 @@ export function CMRRTargetChoices(): Choice[] { label: "Memorized (Default)", value: CMRRTarget.memorized, }, + { + label: "Memorized (Double cost)", + values: CMRRTarget.loss_aversion, + }, { label: "Stability (Experimental)", value: CMRRTarget.stability,