From c4ad27a2db04ed915c8b93c53cd1888e65f9cdcc Mon Sep 17 00:00:00 2001 From: Jarrett Ye Date: Thu, 9 Jan 2025 19:49:13 +0800 Subject: [PATCH] Feat/support new cards ignore review limit in simulator (#3707) * Feat/support new cards ignore review limit in simulator * ./ninja fix:minilints & ./ninja format * use published crate * make newCardsIgnoreReviewLimit reactive * format --------- Co-authored-by: Damien Elmes --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- cargo/licenses.json | 2 +- proto/anki/scheduler.proto | 1 + rslib/src/scheduler/fsrs/params.rs | 3 ++- rslib/src/scheduler/fsrs/retention.rs | 1 + rslib/src/scheduler/fsrs/simulator.rs | 24 +++++++++++++---------- rslib/src/scheduler/service/mod.rs | 3 ++- ts/routes/deck-options/FsrsOptions.svelte | 5 +++++ 9 files changed, 29 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9f284cdb..8dfef6f11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1860,9 +1860,9 @@ dependencies = [ [[package]] name = "fsrs" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "738a17a9825de16c23063c5931738e96aacce208a9b0ab54260c6ea2aa6a6feb" +checksum = "b444b46751c2196ed329f3e577e8a693b948d9d0f07c237473c15dead8d7000e" dependencies = [ "burn", "itertools 0.12.1", diff --git a/Cargo.toml b/Cargo.toml index 625fcf776..b4f0e49e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ git = "https://github.com/ankitects/linkcheck.git" rev = "184b2ca50ed39ca43da13f0b830a463861adb9ca" [workspace.dependencies.fsrs] -version = "=1.5.0" +version = "=2.0.0" # git = "https://github.com/open-spaced-repetition/fsrs-rs.git" # rev = "58ca25ed2bc4bb1dc376208bbcaed7f5a501b941" # path = "../open-spaced-repetition/fsrs-rs" diff --git a/cargo/licenses.json b/cargo/licenses.json index 065fada91..8d6977b22 100644 --- a/cargo/licenses.json +++ b/cargo/licenses.json @@ -1225,7 +1225,7 @@ }, { "name": "fsrs", - "version": "1.5.0", + "version": "2.0.0", "authors": "Open Spaced Repetition", "repository": "https://github.com/open-spaced-repetition/fsrs-rs", "license": "BSD-3-Clause", diff --git a/proto/anki/scheduler.proto b/proto/anki/scheduler.proto index 947a7812c..426045fef 100644 --- a/proto/anki/scheduler.proto +++ b/proto/anki/scheduler.proto @@ -388,6 +388,7 @@ message SimulateFsrsReviewRequest { uint32 review_limit = 6; uint32 max_interval = 7; string search = 8; + bool new_cards_ignore_review_limit = 9; } message SimulateFsrsReviewResponse { diff --git a/rslib/src/scheduler/fsrs/params.rs b/rslib/src/scheduler/fsrs/params.rs index 531bfbd77..a5b916746 100644 --- a/rslib/src/scheduler/fsrs/params.rs +++ b/rslib/src/scheduler/fsrs/params.rs @@ -97,7 +97,8 @@ impl Collection { } } }); - let mut params = FSRS::new(None)?.compute_parameters(items.clone(), Some(progress2))?; + let mut params = + FSRS::new(None)?.compute_parameters(items.clone(), Some(progress2), true)?; progress_thread.join().ok(); if let Ok(fsrs) = FSRS::new(Some(current_params)) { let current_rmse = fsrs.evaluate(items.clone(), |_| true)?.rmse_bins; diff --git a/rslib/src/scheduler/fsrs/retention.rs b/rslib/src/scheduler/fsrs/retention.rs index a408b6013..39b2e6f5d 100644 --- a/rslib/src/scheduler/fsrs/retention.rs +++ b/rslib/src/scheduler/fsrs/retention.rs @@ -53,6 +53,7 @@ impl Collection { loss_aversion: req.loss_aversion as f32, learn_limit, review_limit: usize::MAX, + new_cards_ignore_review_limit: true, }, &req.params, |ip| { diff --git a/rslib/src/scheduler/fsrs/simulator.rs b/rslib/src/scheduler/fsrs/simulator.rs index d9fc2bafa..7af0e1b34 100644 --- a/rslib/src/scheduler/fsrs/simulator.rs +++ b/rslib/src/scheduler/fsrs/simulator.rs @@ -46,13 +46,9 @@ impl Collection { loss_aversion: 1.0, learn_limit: req.new_limit as usize, review_limit: req.review_limit as usize, + new_cards_ignore_review_limit: req.new_cards_ignore_review_limit, }; - let ( - accumulated_knowledge_acquisition, - daily_review_count, - daily_new_count, - daily_time_cost, - ) = simulate( + let result = simulate( &config, &req.params, req.desired_retention, @@ -60,10 +56,18 @@ impl Collection { Some(converted_cards), )?; Ok(SimulateFsrsReviewResponse { - accumulated_knowledge_acquisition: accumulated_knowledge_acquisition.to_vec(), - daily_review_count: daily_review_count.iter().map(|x| *x as u32).collect_vec(), - daily_new_count: daily_new_count.iter().map(|x| *x as u32).collect_vec(), - daily_time_cost: daily_time_cost.to_vec(), + accumulated_knowledge_acquisition: result.memorized_cnt_per_day.to_vec(), + daily_review_count: result + .review_cnt_per_day + .iter() + .map(|x| *x as u32) + .collect_vec(), + daily_new_count: result + .learn_cnt_per_day + .iter() + .map(|x| *x as u32) + .collect_vec(), + daily_time_cost: result.cost_per_day.to_vec(), }) } } diff --git a/rslib/src/scheduler/service/mod.rs b/rslib/src/scheduler/service/mod.rs index 59a9aa503..b725c6b95 100644 --- a/rslib/src/scheduler/service/mod.rs +++ b/rslib/src/scheduler/service/mod.rs @@ -348,6 +348,7 @@ impl crate::services::BackendSchedulerService for Backend { let params = fsrs.compute_parameters( req.items.into_iter().map(fsrs_item_proto_to_fsrs).collect(), None, + true, )?; Ok(ComputeFsrsParamsResponse { params, fsrs_items }) } @@ -362,7 +363,7 @@ impl crate::services::BackendSchedulerService for Backend { .into_iter() .map(fsrs_item_proto_to_fsrs) .collect(); - let params = fsrs.benchmark(train_set); + let params = fsrs.benchmark(train_set, true); Ok(FsrsBenchmarkResponse { params }) } diff --git a/ts/routes/deck-options/FsrsOptions.svelte b/ts/routes/deck-options/FsrsOptions.svelte index 2ea823161..ac79fa2f7 100644 --- a/ts/routes/deck-options/FsrsOptions.svelte +++ b/ts/routes/deck-options/FsrsOptions.svelte @@ -90,6 +90,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html optimalRetentionRequest.daysToSimulate = 3650; } + $: newCardsIgnoreReviewLimit = state.newCardsIgnoreReviewLimit; + const simulateFsrsRequest = new SimulateFsrsReviewRequest({ params: fsrsParams($config), desiredRetention: $config.desiredRetention, @@ -99,6 +101,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html reviewLimit: $config.reviewsPerDay, maxInterval: $config.maximumReviewInterval, search: `preset:"${state.getCurrentNameForSearch()}" -is:suspended`, + newCardsIgnoreReviewLimit: $newCardsIgnoreReviewLimit, }); function getRetentionWarning(retention: number): string { @@ -318,6 +321,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html simulateFsrsRequest.params = fsrsParams($config); simulateFsrsRequest.desiredRetention = $config.desiredRetention; simulateFsrsRequest.search = `preset:"${state.getCurrentNameForSearch()}" -is:suspended`; + simulateFsrsRequest.newCardsIgnoreReviewLimit = + $newCardsIgnoreReviewLimit; simulating = true; resp = await simulateFsrsReview(simulateFsrsRequest); },