diff --git a/Cargo.lock b/Cargo.lock index 079b85695..86787124a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2213,8 +2213,9 @@ dependencies = [ [[package]] name = "fsrs" -version = "5.0.0" -source = "git+https://github.com/open-spaced-repetition/fsrs-rs.git?branch=Refactor/expected_workload_via_dp#808bd715b302ccdda26a2d29c7566270e7be3bb3" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1f3a8c3df2c324ebab71461178fe8c1fe2d7373cf603f312b652befd026f06d" dependencies = [ "burn", "itertools 0.14.0", diff --git a/Cargo.toml b/Cargo.toml index 212c79ea1..2ff29cd1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,9 +33,8 @@ git = "https://github.com/ankitects/linkcheck.git" rev = "184b2ca50ed39ca43da13f0b830a463861adb9ca" [workspace.dependencies.fsrs] -# version = "4.1.1" -git = "https://github.com/open-spaced-repetition/fsrs-rs.git" -branch = "Refactor/expected_workload_via_dp" +version = "4.1.1" +# git = "https://github.com/open-spaced-repetition/fsrs-rs.git" # rev = "a7f7efc10f0a26b14ee348cc7402155685f2a24f" # path = "../open-spaced-repetition/fsrs-rs" diff --git a/rslib/src/deckconfig/service.rs b/rslib/src/deckconfig/service.rs index ba2bba3a1..21bcfd676 100644 --- a/rslib/src/deckconfig/service.rs +++ b/rslib/src/deckconfig/service.rs @@ -103,15 +103,43 @@ impl crate::services::DeckConfigService for Collection { &mut self, input: anki_proto::deck_config::GetRetentionWorkloadRequest, ) -> Result { + const LEARN_SPAN: usize = 365; + const TERMINATION_PROB: f32 = 1e-5; + // the default values are from https://github.com/open-spaced-repetition/Anki-button-usage/blob/881009015c2a85ac911021d76d0aacb124849937/analysis.ipynb + const DEFAULT_LEARN_COST: f32 = 19.4698; + const DEFAULT_PASS_COST: f32 = 7.8454; + const DEFAULT_FAIL_COST: f32 = 23.185; + const DEFAULT_INITIAL_PASS_RATE: f32 = 0.7645; + let guard = self.search_cards_into_table(&input.search, crate::search::SortMode::NoOrder)?; - - let revlogs = guard - .col - .storage - .get_revlog_entries_for_searched_cards_in_card_order()?; + let costs = guard.col.storage.get_costs_for_retention()?; - let config = guard.col.get_optimal_retention_parameters(revlogs)?; + fn smoothing(obs: f32, default: f32, count: u32) -> f32 { + let alpha = count as f32 / (50.0 + count as f32); + obs * alpha + default * (1.0 - alpha) + } + + let cost_success = smoothing( + costs.average_pass_time_ms / 1000.0, + DEFAULT_PASS_COST, + costs.pass_count, + ); + let cost_failure = smoothing( + costs.average_fail_time_ms / 1000.0, + DEFAULT_FAIL_COST, + costs.fail_count, + ); + let cost_learn = smoothing( + costs.average_learn_time_ms / 1000.0, + DEFAULT_LEARN_COST, + costs.learn_count, + ); + let initial_pass_rate = smoothing( + costs.initial_pass_rate, + DEFAULT_INITIAL_PASS_RATE, + costs.pass_count, + ); let costs = (70u32..=99u32) .map(|dr| { @@ -120,7 +148,12 @@ impl crate::services::DeckConfigService for Collection { fsrs::expected_workload( &input.w, dr as f32 / 100., - &config + LEARN_SPAN, + cost_success, + cost_failure, + cost_learn, + initial_pass_rate, + TERMINATION_PROB, )?, )) })