Skip to content

Commit

Permalink
Merge branch 'main' into Feat/recency-weighting
Browse files Browse the repository at this point in the history
  • Loading branch information
L-M-Sherlock authored Dec 24, 2024
2 parents a5600a8 + 7da138a commit 6bab27a
Showing 1 changed file with 74 additions and 20 deletions.
94 changes: 74 additions & 20 deletions src/optimal_retention.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,15 @@ pub fn simulate(
.into_iter()
.filter(|card| card.stability > 1e-9),
);

for card in &cards {
let upper = min(card.due as usize, learn_span);
let elapsed_days = -card.last_date;
for i in 0..upper {
memorized_cnt_per_day[i] +=
power_forgetting_curve(elapsed_days + i as f32, card.stability);
}
}
}

if learn_limit > 0 {
Expand All @@ -207,9 +216,9 @@ 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() {
Expand All @@ -229,7 +238,7 @@ pub fn simulate(
card_priorities.pop();
continue;
}
if (review_cnt_per_day[day_index] + 1 > review_limit)
if (!is_learn && review_cnt_per_day[day_index] + 1 > review_limit)
|| (is_learn && learn_cnt_per_day[day_index] + 1 > learn_limit)
|| (cost_per_day[day_index] + fail_cost > max_cost_perday)
{
Expand Down Expand Up @@ -306,12 +315,12 @@ pub fn simulate(
// Update days statistics
review_cnt_per_day[day_index] += 1;
cost_per_day[day_index] += cost;
}

let upper = min(ivl as usize, learn_span - day_index);
for i in 0..upper {
memorized_cnt_per_day[day_index + i] +=
power_forgetting_curve(i as f32, card.stability);
}
let upper = min(ivl as usize, learn_span - day_index);
for i in 0..upper {
memorized_cnt_per_day[day_index + i] +=
power_forgetting_curve(i as f32, card.stability);
}

// dbg!(ivl);
Expand All @@ -324,9 +333,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!((
Expand Down Expand Up @@ -906,11 +913,43 @@ mod tests {
simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, None)?;
assert_eq!(
memorized_cnt_per_day[memorized_cnt_per_day.len() - 1],
6911.91
6781.4946
);
Ok(())
}

#[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: LOWER,
learn_limit: LEARN_LIMIT,
deck_size: DECK_SIZE,
..Default::default()
};
let (_, review_cnt_per_day_lower, _, _) =
simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, None)?;
let config = SimulatorConfig {
learn_span: LOWER + 10,
learn_limit: LEARN_LIMIT,
deck_size: DECK_SIZE,
..Default::default()
};
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_lower[i], review_cnt_per_day_higher[i],
"at index {}",
i
);
}
Ok(())
}

#[test]
fn simulate_with_existing_cards() -> Result<()> {
let config = SimulatorConfig {
Expand All @@ -933,9 +972,24 @@ mod tests {
last_date: -2.0,
due: 0.0,
},
Card {
difficulty: 5.0,
stability: 2.0,
last_date: -2.0,
due: 1.0,
},
Card {
difficulty: 5.0,
stability: 2.0,
last_date: -8.0,
due: -1.0,
},
];
let memorization = simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, Some(cards))?;
dbg!(memorization);
let (memorized_cnt_per_day, review_cnt_per_day, learn_cnt_per_day, _) =
simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, Some(cards))?;
assert_eq!(memorized_cnt_per_day[0], 63.9);
assert_eq!(review_cnt_per_day[0], 3);
assert_eq!(learn_cnt_per_day[0], 60);
Ok(())
}

Expand Down Expand Up @@ -1008,8 +1062,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, 178, 163, 151, 160, 151
]
);
assert_eq!(
Expand All @@ -1026,7 +1080,7 @@ mod tests {
..Default::default()
};
let results = simulate(&config, &DEFAULT_PARAMETERS, 0.9, None, None)?;
assert_eq!(results.0[results.0.len() - 1], 6559.517);
assert_eq!(results.0[results.0.len() - 1], 6484.7144);
Ok(())
}

Expand Down Expand Up @@ -1079,7 +1133,7 @@ mod tests {
..Default::default()
};
let optimal_retention = fsrs.optimal_retention(&config, &[], |_v| true).unwrap();
assert_eq!(optimal_retention, 0.84458643);
assert_eq!(optimal_retention, 0.85450846);
assert!(fsrs.optimal_retention(&config, &[1.], |_v| true).is_err());
Ok(())
}
Expand All @@ -1099,7 +1153,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, &param, |_v| true).unwrap();
assert_eq!(optimal_retention, 0.85450846);
assert_eq!(optimal_retention, 0.83750373);
Ok(())
}

Expand Down

0 comments on commit 6bab27a

Please sign in to comment.