Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/re-optimize FSRS if short-term param is weird #3742

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions proto/anki/scheduler.proto
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ message ComputeFsrsParamsRequest {
string search = 1;
repeated float current_params = 2;
int64 ignore_revlogs_before_ms = 3;
uint32 num_of_relearning_steps = 4;
}

message ComputeFsrsParamsResponse {
Expand Down
2 changes: 2 additions & 0 deletions rslib/src/deckconfig/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,14 @@ impl Collection {
config.inner.param_search.clone()
};
let ignore_revlogs_before_ms = ignore_revlogs_before_ms_from_config(config)?;
let num_of_relearning_steps = config.inner.relearn_steps.len();
match self.compute_params(
&search,
ignore_revlogs_before_ms,
idx as u32 + 1,
config_len,
config.fsrs_params(),
num_of_relearning_steps,
) {
Ok(params) => {
println!("{}: {:?}", config.name, params.params);
Expand Down
11 changes: 11 additions & 0 deletions rslib/src/progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ pub struct ProgressState {
pub last_progress: Option<Progress>,
}

impl ProgressState {
pub fn reset(&mut self) {
self.want_abort = false;
self.last_progress = None;
}
}

#[derive(Clone, Copy, Debug)]
pub enum Progress {
MediaSync(MediaSyncProgress),
Expand Down Expand Up @@ -320,6 +327,10 @@ impl Collection {
) -> ThrottlingProgressHandler<P> {
ThrottlingProgressHandler::new(self.state.progress.clone())
}

pub(crate) fn clear_progress(&mut self) {
self.state.progress.lock().unwrap().reset();
}
}

pub(crate) struct Incrementor<'f, F: 'f + FnMut(usize) -> Result<()>> {
Expand Down
78 changes: 54 additions & 24 deletions rslib/src/scheduler/fsrs/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use chrono::NaiveTime;
use fsrs::CombinedProgressState;
use fsrs::FSRSItem;
use fsrs::FSRSReview;
use fsrs::MemoryState;
use fsrs::ModelEvaluation;
use fsrs::FSRS;
use itertools::Itertools;
Expand Down Expand Up @@ -60,8 +61,9 @@ impl Collection {
current_preset: u32,
total_presets: u32,
current_params: &Params,
num_of_relearning_steps: usize,
) -> Result<ComputeFsrsParamsResponse> {
let mut anki_progress = self.new_progress_handler::<ComputeParamsProgress>();
self.clear_progress();
let timing = self.timing_today()?;
let revlogs = self.revlog_for_srs(search)?;
let (items, review_count) =
Expand All @@ -74,31 +76,38 @@ impl Collection {
fsrs_items,
});
}
anki_progress.update(false, |p| {
p.current_preset = current_preset;
p.total_presets = total_presets;
})?;
// adapt the progress handler to our built-in progress handling
let progress = CombinedProgressState::new_shared();
let progress2 = progress.clone();
let progress_thread = thread::spawn(move || {
let mut finished = false;
while !finished {
thread::sleep(Duration::from_millis(100));
let mut guard = progress.lock().unwrap();
if let Err(_err) = anki_progress.update(false, |s| {
s.total_iterations = guard.total() as u32;
s.current_iteration = guard.current() as u32;
s.reviews = review_count as u32;
finished = guard.finished();
}) {
guard.want_abort = true;
return;

let create_progress_thread = || -> Result<_> {
let mut anki_progress = self.new_progress_handler::<ComputeParamsProgress>();
anki_progress.update(false, |p| {
p.current_preset = current_preset;
p.total_presets = total_presets;
})?;
let progress = CombinedProgressState::new_shared();
let progress2 = progress.clone();
let progress_thread = thread::spawn(move || {
let mut finished = false;
while !finished {
thread::sleep(Duration::from_millis(100));
let mut guard = progress.lock().unwrap();
if let Err(_err) = anki_progress.update(false, |s| {
s.total_iterations = guard.total() as u32;
s.current_iteration = guard.current() as u32;
s.reviews = review_count as u32;
finished = guard.finished();
}) {
guard.want_abort = true;
return;
}
}
}
});
let mut params =
FSRS::new(None)?.compute_parameters(items.clone(), Some(progress2), true)?;
});
Ok((progress2, progress_thread))
};

let (progress, progress_thread) = create_progress_thread()?;
let fsrs = FSRS::new(None)?;
let mut params = fsrs.compute_parameters(items.clone(), Some(progress.clone()), true)?;
progress_thread.join().ok();
if let Ok(fsrs) = FSRS::new(Some(current_params)) {
let current_rmse = fsrs.evaluate(items.clone(), |_| true)?.rmse_bins;
Expand All @@ -107,6 +116,27 @@ impl Collection {
if current_rmse <= optimized_rmse {
params = current_params.to_vec();
}
if num_of_relearning_steps > 1 {
let memory_state = MemoryState {
stability: 1.0,
difficulty: 1.0,
};
let s_fail = optimized_fsrs
.next_states(Some(memory_state), 0.9, 2)?
.again;
let mut s_short_term = s_fail.memory;
for _ in 0..num_of_relearning_steps {
s_short_term = optimized_fsrs
.next_states(Some(s_short_term), 0.9, 0)?
.good
.memory;
}
if s_short_term.stability > memory_state.stability {
let (progress, progress_thread) = create_progress_thread()?;
params = fsrs.compute_parameters(items.clone(), Some(progress), false)?;
progress_thread.join().ok();
}
}
}

Ok(ComputeFsrsParamsResponse { params, fsrs_items })
Expand Down
1 change: 1 addition & 0 deletions rslib/src/scheduler/service/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ impl crate::services::SchedulerService for Collection {
1,
1,
&input.current_params,
input.num_of_relearning_steps as usize,
)
}

Expand Down
11 changes: 11 additions & 0 deletions ts/routes/deck-options/FsrsOptions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,23 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
await runWithBackendProgress(
async () => {
const params = fsrsParams($config);
const RelearningSteps = $config.relearnSteps;
let numOfRelearningStepsInDay = 0;
let accumulatedTime = 0;
for (let i = 0; i < RelearningSteps.length; i++) {
accumulatedTime += RelearningSteps[i];
if (accumulatedTime >= 1440) {
break;
}
numOfRelearningStepsInDay++;
}
const resp = await computeFsrsParams({
search: $config.paramSearch
? $config.paramSearch
: defaultparamSearch,
ignoreRevlogsBeforeMs: getIgnoreRevlogsBeforeMs(),
currentParams: params,
numOfRelearningSteps: numOfRelearningStepsInDay,
});

const already_optimal =
Expand Down