From 6321eefef2fa8e0f53d0e8c1cd070feed24e8209 Mon Sep 17 00:00:00 2001 From: Jarrett Ye Date: Mon, 23 Dec 2024 15:31:14 +0800 Subject: [PATCH 1/7] Fix/optimize card priority calculation and fix learn span behavior --- src/optimal_retention.rs | 46 ++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/optimal_retention.rs b/src/optimal_retention.rs index 20cb6ec..644c741 100644 --- a/src/optimal_retention.rs +++ b/src/optimal_retention.rs @@ -216,12 +216,15 @@ pub fn simulate( let mut card_priorities = PriorityQueue::new(); - fn card_priority(card: &Card, learn: bool) -> (usize, bool, usize) { - // High difficulty priority as example - (-card.due as usize, !learn, -card.difficulty as usize) + fn card_priority(card: &Card, learn: bool) -> (i32, bool, i32) { + // high priority for early due, review, low difficulty card + (-card.due as i32, !learn, -(card.difficulty * 100.0) as i32) } for (i, card) in cards.iter().enumerate() { + if card.due >= learn_span as f32 { + continue; + } card_priorities.push(i, card_priority(card, card.last_date == f32::NEG_INFINITY)); } @@ -915,11 +918,36 @@ mod tests { simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, None)?; assert_eq!( memorized_cnt_per_day[memorized_cnt_per_day.len() - 1], - 7004.319 + 6898.48 ); Ok(()) } + #[test] + fn changing_learn_span_should_get_same_review_cnt_per_day() -> Result<()> { + let config = SimulatorConfig { + learn_span: 10, + learn_limit: 10, + deck_size: 200, + ..Default::default() + }; + let (_, review_cnt_per_day_10, _, _) = + simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, None)?; + let config = SimulatorConfig { + learn_span: 11, + learn_limit: 10, + deck_size: 200, + ..Default::default() + }; + let (_, review_cnt_per_day_11, _, _) = + simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, None)?; + // Compare first 10 items of review_cnt_per_day arrays + for i in 0..10 { + assert_eq!(review_cnt_per_day_10[i], review_cnt_per_day_11[i]); + } + Ok(()) + } + #[test] fn simulate_with_existing_cards() -> Result<()> { let config = SimulatorConfig { @@ -1032,8 +1060,8 @@ mod tests { assert_eq!( results.1.to_vec(), vec![ - 0, 16, 25, 34, 60, 65, 76, 85, 91, 92, 100, 103, 119, 107, 103, 113, 122, 143, 149, - 151, 148, 172, 154, 175, 156, 169, 155, 191, 185, 170 + 0, 15, 18, 38, 64, 64, 80, 89, 95, 95, 100, 96, 107, 118, 120, 114, 126, 123, 139, + 167, 158, 156, 167, 161, 154, 177, 162, 148, 165, 156 ] ); assert_eq!( @@ -1050,7 +1078,7 @@ mod tests { ..Default::default() }; let results = simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, None)?; - assert_eq!(results.0[results.0.len() - 1], 6619.07); + assert_eq!(results.0[results.0.len() - 1], 6460.082); Ok(()) } @@ -1103,7 +1131,7 @@ mod tests { ..Default::default() }; let optimal_retention = fsrs.optimal_retention(&config, &[], |_v| true).unwrap(); - assert_eq!(optimal_retention, 0.8372485); + assert_eq!(optimal_retention, 0.85450846); assert!(fsrs.optimal_retention(&config, &[1.], |_v| true).is_err()); Ok(()) } @@ -1123,7 +1151,7 @@ mod tests { let mut param = DEFAULT_PARAMETERS[..17].to_vec(); param.extend_from_slice(&[0.0, 0.0]); let optimal_retention = fsrs.optimal_retention(&config, ¶m, |_v| true).unwrap(); - assert_eq!(optimal_retention, 0.85450846); + assert_eq!(optimal_retention, 0.83750373); Ok(()) } From 8d0204f2bdbd37d74ef08d43b53193309fa2fefb Mon Sep 17 00:00:00 2001 From: Jarrett Ye Date: Mon, 23 Dec 2024 15:31:39 +0800 Subject: [PATCH 2/7] bump version --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5481a97..7f9d23f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1077,7 +1077,7 @@ dependencies = [ [[package]] name = "fsrs" -version = "1.4.7" +version = "1.4.8" dependencies = [ "burn", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 52ce055..0d7b174 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fsrs" -version = "1.4.7" +version = "1.4.8" authors = ["Open Spaced Repetition"] categories = ["algorithms", "science"] edition = "2021" From ed87a7748e082f725ff739efaa7f5dc8ba1b9a34 Mon Sep 17 00:00:00 2001 From: Jarrett Ye Date: Mon, 23 Dec 2024 23:12:43 +0800 Subject: [PATCH 3/7] update unit test Co-authored-by: Luc Mcgrady --- src/optimal_retention.rs | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/optimal_retention.rs b/src/optimal_retention.rs index 644c741..81a8021 100644 --- a/src/optimal_retention.rs +++ b/src/optimal_retention.rs @@ -925,29 +925,35 @@ mod tests { #[test] fn changing_learn_span_should_get_same_review_cnt_per_day() -> Result<()> { + const LOWER: usize = 365; + const DECK_SIZE: usize = 1000; + const LEARN_LIMIT: usize = 10; let config = SimulatorConfig { - learn_span: 10, - learn_limit: 10, - deck_size: 200, + learn_span: LOWER, + learn_limit: LEARN_LIMIT, + deck_size: DECK_SIZE, ..Default::default() }; let (_, review_cnt_per_day_10, _, _) = simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, None)?; let config = SimulatorConfig { - learn_span: 11, - learn_limit: 10, - deck_size: 200, + learn_span: LOWER + 10, + learn_limit: LEARN_LIMIT, + deck_size: DECK_SIZE, ..Default::default() }; let (_, review_cnt_per_day_11, _, _) = simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, None)?; - // Compare first 10 items of review_cnt_per_day arrays - for i in 0..10 { - assert_eq!(review_cnt_per_day_10[i], review_cnt_per_day_11[i]); + // Compare first LOWER items of review_cnt_per_day arrays + for i in 0..LOWER { + assert_eq!( + review_cnt_per_day_10[i], review_cnt_per_day_11[i], + "at index {}", + i + ); } Ok(()) - } - +} #[test] fn simulate_with_existing_cards() -> Result<()> { let config = SimulatorConfig { From 2bfd5f9a0fee38175208c78a0376e9f73975c24e Mon Sep 17 00:00:00 2001 From: Jarrett Ye Date: Mon, 23 Dec 2024 23:15:39 +0800 Subject: [PATCH 4/7] cargo fmt --- src/optimal_retention.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/optimal_retention.rs b/src/optimal_retention.rs index 81a8021..0e76a7d 100644 --- a/src/optimal_retention.rs +++ b/src/optimal_retention.rs @@ -953,7 +953,7 @@ mod tests { ); } Ok(()) -} + } #[test] fn simulate_with_existing_cards() -> Result<()> { let config = SimulatorConfig { From 669416153f6d125b0a18ce8f490b046d43affa52 Mon Sep 17 00:00:00 2001 From: Jarrett Ye Date: Mon, 23 Dec 2024 23:54:37 +0800 Subject: [PATCH 5/7] pass unit test --- src/optimal_retention.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/optimal_retention.rs b/src/optimal_retention.rs index 0e76a7d..7343b81 100644 --- a/src/optimal_retention.rs +++ b/src/optimal_retention.rs @@ -336,9 +336,7 @@ pub fn simulate( card.last_date = card.due; card.due += ivl; - if (card.due as usize) <= learn_span { - card_priorities.change_priority(&card_index, card_priority(card, false)); - } + card_priorities.change_priority(&card_index, card_priority(card, false)); } /*dbg!(( @@ -934,7 +932,7 @@ mod tests { deck_size: DECK_SIZE, ..Default::default() }; - let (_, review_cnt_per_day_10, _, _) = + let (_, review_cnt_per_day_lower, _, _) = simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, None)?; let config = SimulatorConfig { learn_span: LOWER + 10, @@ -942,18 +940,19 @@ mod tests { deck_size: DECK_SIZE, ..Default::default() }; - let (_, review_cnt_per_day_11, _, _) = + let (_, review_cnt_per_day_higher, _, _) = simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, None)?; // Compare first LOWER items of review_cnt_per_day arrays for i in 0..LOWER { assert_eq!( - review_cnt_per_day_10[i], review_cnt_per_day_11[i], + review_cnt_per_day_lower[i], review_cnt_per_day_higher[i], "at index {}", i ); } Ok(()) } + #[test] fn simulate_with_existing_cards() -> Result<()> { let config = SimulatorConfig { From 5c7ad53ccf7272a17876710006f6003a2efd7c1f Mon Sep 17 00:00:00 2001 From: Jarrett Ye Date: Mon, 23 Dec 2024 23:57:50 +0800 Subject: [PATCH 6/7] update unit test --- src/optimal_retention.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/optimal_retention.rs b/src/optimal_retention.rs index 7343b81..d5c060a 100644 --- a/src/optimal_retention.rs +++ b/src/optimal_retention.rs @@ -916,7 +916,7 @@ mod tests { simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, None)?; assert_eq!( memorized_cnt_per_day[memorized_cnt_per_day.len() - 1], - 6898.48 + 6781.4946 ); Ok(()) } @@ -1066,7 +1066,7 @@ mod tests { results.1.to_vec(), vec![ 0, 15, 18, 38, 64, 64, 80, 89, 95, 95, 100, 96, 107, 118, 120, 114, 126, 123, 139, - 167, 158, 156, 167, 161, 154, 177, 162, 148, 165, 156 + 167, 158, 156, 167, 161, 154, 178, 163, 151, 160, 151 ] ); assert_eq!( @@ -1083,7 +1083,7 @@ mod tests { ..Default::default() }; let results = simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, None)?; - assert_eq!(results.0[results.0.len() - 1], 6460.082); + assert_eq!(results.0[results.0.len() - 1], 6484.7144); Ok(()) } From 8528600e7a13b3a12862592e0aac1860fd8d9215 Mon Sep 17 00:00:00 2001 From: Jarrett Ye Date: Tue, 24 Dec 2024 10:03:37 +0800 Subject: [PATCH 7/7] Update src/optimal_retention.rs Co-authored-by: Luc Mcgrady --- src/optimal_retention.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/optimal_retention.rs b/src/optimal_retention.rs index d5c060a..4550066 100644 --- a/src/optimal_retention.rs +++ b/src/optimal_retention.rs @@ -222,9 +222,6 @@ pub fn simulate( } for (i, card) in cards.iter().enumerate() { - if card.due >= learn_span as f32 { - continue; - } card_priorities.push(i, card_priority(card, card.last_date == f32::NEG_INFINITY)); }