Anki/rslib/src/stats/graphs/retrievability.rs
Jarrett Ye e096c462fa
Feat/FSRS-6 (#3929)
* Feat/FSRS-6

* update comment

* add decay to Card

* ./ninja fix:minilints

* pass check

* fix NaN in evaluation

* remove console

* decay should fallback to 0.5 when it's None.

* Update SimulatorModal.svelte

* Update a few comments

* Update FSRS decay defaults to use constants for better maintainability and clarity

* Update rslib/src/storage/card/data.rs
2025-04-25 16:44:34 +10:00

61 lines
2.3 KiB
Rust

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use anki_proto::stats::graphs_response::Retrievability;
use fsrs::FSRS;
use fsrs::FSRS5_DEFAULT_DECAY;
use crate::prelude::TimestampSecs;
use crate::scheduler::timing::SchedTimingToday;
use crate::stats::graphs::eases::percent_to_bin;
use crate::stats::graphs::GraphsContext;
impl GraphsContext {
/// (SM-2, FSRS)
pub(super) fn retrievability(&self) -> Retrievability {
let mut retrievability = Retrievability::default();
let mut card_with_retrievability_count: usize = 0;
let timing = SchedTimingToday {
days_elapsed: self.days_elapsed,
now: TimestampSecs::now(),
next_day_at: self.next_day_start,
};
let fsrs = FSRS::new(None).unwrap();
// note id -> (sum, count)
let mut note_retrievability: std::collections::HashMap<i64, (f32, u32)> =
std::collections::HashMap::new();
for card in &self.cards {
let entry = note_retrievability
.entry(card.note_id.0)
.or_insert((0.0, 0));
entry.1 += 1;
if let Some(state) = card.memory_state {
let elapsed_days = card.days_since_last_review(&timing).unwrap_or_default();
let r = fsrs.current_retrievability(
state.into(),
elapsed_days,
card.decay.unwrap_or(FSRS5_DEFAULT_DECAY),
);
*retrievability
.retrievability
.entry(percent_to_bin(r * 100.0))
.or_insert_with(Default::default) += 1;
retrievability.sum_by_card += r;
card_with_retrievability_count += 1;
entry.0 += r;
} else {
entry.0 += 0.0;
}
}
if card_with_retrievability_count != 0 {
retrievability.average =
retrievability.sum_by_card * 100.0 / card_with_retrievability_count as f32;
}
retrievability.sum_by_note = note_retrievability
.values()
.map(|(sum, count)| sum / *count as f32)
.sum();
retrievability
}
}