Rename remaining 'weights' references to 'params'

This commit is contained in:
Damien Elmes 2024-10-21 18:10:08 +10:00
parent c45fa518d2
commit 6adbd922f7
27 changed files with 194 additions and 200 deletions

View file

@ -127,21 +127,21 @@ message Progress {
DatabaseCheck database_check = 6;
string importing = 7;
string exporting = 8;
ComputeWeightsProgress compute_weights = 9;
ComputeParamsProgress compute_params = 9;
ComputeRetentionProgress compute_retention = 10;
ComputeMemoryProgress compute_memory = 11;
}
}
message ComputeWeightsProgress {
message ComputeParamsProgress {
// Current iteration
uint32 current = 1;
// Total iterations
uint32 total = 2;
uint32 reviews = 3;
// Only used in 'compute all weights' case
// Only used in 'compute all params' case
uint32 current_preset = 4;
// Only used in 'compute all weights' case
// Only used in 'compute all params' case
uint32 total_presets = 5;
}

View file

@ -165,7 +165,7 @@ message DeckConfig {
// used for fsrs_reschedule in the past
reserved 39;
float historical_retention = 40;
string weight_search = 45;
string param_search = 45;
bytes other = 255;
}
@ -215,7 +215,7 @@ message DeckConfigsForUpdate {
enum UpdateDeckConfigsMode {
UPDATE_DECK_CONFIGS_MODE_NORMAL = 0;
UPDATE_DECK_CONFIGS_MODE_APPLY_TO_CHILDREN = 1;
UPDATE_DECK_CONFIGS_MODE_COMPUTE_ALL_WEIGHTS = 2;
UPDATE_DECK_CONFIGS_MODE_COMPUTE_ALL_PARAMS = 2;
}
message UpdateDeckConfigsRequest {

View file

@ -45,15 +45,15 @@ service SchedulerService {
rpc CustomStudyDefaults(CustomStudyDefaultsRequest)
returns (CustomStudyDefaultsResponse);
rpc RepositionDefaults(generic.Empty) returns (RepositionDefaultsResponse);
rpc ComputeFsrsWeights(ComputeFsrsWeightsRequest)
returns (ComputeFsrsWeightsResponse);
rpc ComputeFsrsParams(ComputeFsrsParamsRequest)
returns (ComputeFsrsParamsResponse);
rpc GetOptimalRetentionParameters(GetOptimalRetentionParametersRequest)
returns (GetOptimalRetentionParametersResponse);
rpc ComputeOptimalRetention(ComputeOptimalRetentionRequest)
returns (ComputeOptimalRetentionResponse);
rpc SimulateFsrsReview(SimulateFsrsReviewRequest)
returns (SimulateFsrsReviewResponse);
rpc EvaluateWeights(EvaluateWeightsRequest) returns (EvaluateWeightsResponse);
rpc EvaluateParams(EvaluateParamsRequest) returns (EvaluateParamsResponse);
rpc ComputeMemoryState(cards.CardId) returns (ComputeMemoryStateResponse);
// The number of days the calculated interval was fuzzed by on the previous
// review (if any). Utilized by the FSRS add-on.
@ -63,8 +63,8 @@ service SchedulerService {
// Implicitly includes any of the above methods that are not listed in the
// backend service.
service BackendSchedulerService {
rpc ComputeFsrsWeightsFromItems(ComputeFsrsWeightsFromItemsRequest)
returns (ComputeFsrsWeightsResponse);
rpc ComputeFsrsParamsFromItems(ComputeFsrsParamsFromItemsRequest)
returns (ComputeFsrsParamsResponse);
// Generates parameters used for FSRS's scheduler benchmarks.
rpc FsrsBenchmark(FsrsBenchmarkRequest) returns (FsrsBenchmarkResponse);
// Used for exporting revlogs for algorithm research.
@ -341,19 +341,19 @@ message RepositionDefaultsResponse {
bool shift = 2;
}
message ComputeFsrsWeightsRequest {
message ComputeFsrsParamsRequest {
/// The search used to gather cards for training
string search = 1;
repeated float current_weights = 2;
repeated float current_params = 2;
int64 ignore_revlogs_before_ms = 3;
}
message ComputeFsrsWeightsResponse {
repeated float weights = 1;
message ComputeFsrsParamsResponse {
repeated float params = 1;
uint32 fsrs_items = 2;
}
message ComputeFsrsWeightsFromItemsRequest {
message ComputeFsrsParamsFromItemsRequest {
repeated FsrsItem items = 1;
}
@ -362,7 +362,7 @@ message FsrsBenchmarkRequest {
}
message FsrsBenchmarkResponse {
repeated float weights = 1;
repeated float params = 1;
}
message ExportDatasetRequest {
@ -380,7 +380,7 @@ message FsrsReview {
}
message SimulateFsrsReviewRequest {
repeated float weights = 1;
repeated float params = 1;
float desired_retention = 2;
uint32 deck_size = 3;
uint32 days_to_simulate = 4;
@ -398,7 +398,7 @@ message SimulateFsrsReviewResponse {
}
message ComputeOptimalRetentionRequest {
repeated float weights = 1;
repeated float params = 1;
uint32 days_to_simulate = 2;
uint32 max_interval = 3;
string search = 4;
@ -431,13 +431,13 @@ message GetOptimalRetentionParametersResponse {
uint32 review_limit = 15;
}
message EvaluateWeightsRequest {
repeated float weights = 1;
message EvaluateParamsRequest {
repeated float params = 1;
string search = 2;
int64 ignore_revlogs_before_ms = 3;
}
message EvaluateWeightsResponse {
message EvaluateParamsResponse {
float log_loss = 1;
float rmse_bins = 2;
}

View file

@ -149,8 +149,8 @@ class RustBackend(RustBackendGenerated):
)
return self.format_timespan(seconds=seconds, context=context)
def compute_weights_from_items(self, items: Iterable[FsrsItem]) -> Sequence[float]:
return self.compute_fsrs_weights_from_items(items).weights
def compute_params_from_items(self, items: Iterable[FsrsItem]) -> Sequence[float]:
return self.compute_fsrs_params_from_items(items).params
def benchmark(self, train_set: Iterable[FsrsItem]) -> Sequence[float]:
return self.fsrs_benchmark(train_set=train_set)

View file

@ -457,8 +457,8 @@ def update_deck_configs() -> bytes:
update.max = val.total_cards
update.value = val.current_cards
update.label = val.label
elif progress.HasField("compute_weights"):
val2 = progress.compute_weights
elif progress.HasField("compute_params"):
val2 = progress.compute_params
# prevent an indeterminate progress bar from appearing at the start of each preset
update.max = max(val2.total, 1)
update.value = val2.current
@ -621,10 +621,10 @@ exposed_backend_list = [
"update_image_occlusion_note",
"get_image_occlusion_fields",
# SchedulerService
"compute_fsrs_weights",
"compute_fsrs_params",
"compute_optimal_retention",
"set_wants_abort",
"evaluate_weights",
"evaluate_params",
"get_optimal_retention_parameters",
"simulate_fsrs_review",
]

View file

@ -42,7 +42,7 @@ impl AnkiError {
AnkiError::InvalidId => Kind::InvalidInput,
AnkiError::InvalidMethodIndex
| AnkiError::InvalidServiceIndex
| AnkiError::FsrsWeightsInvalid
| AnkiError::FsrsParamsInvalid
| AnkiError::FsrsUnableToDetermineDesiredRetention
| AnkiError::FsrsInsufficientData => Kind::InvalidInput,
#[cfg(windows)]

View file

@ -79,7 +79,7 @@ const DEFAULT_DECK_CONFIG_INNER: DeckConfigInner = DeckConfigInner {
desired_retention: 0.9,
other: Vec::new(),
historical_retention: 0.9,
weight_search: String::new(),
param_search: String::new(),
ignore_revlogs_before_date: String::new(),
easy_days_percentages: Vec::new(),
};

View file

@ -94,8 +94,8 @@ pub struct DeckConfSchema11 {
#[serde(default)]
/// historical retention
sm2_retention: f32,
#[serde(default)]
weight_search: String,
#[serde(default, rename = "weightSearch")]
param_search: String,
#[serde(flatten)]
other: HashMap<String, Value>,
@ -312,7 +312,7 @@ impl Default for DeckConfSchema11 {
fsrs_params_5: vec![],
desired_retention: 0.9,
sm2_retention: 0.9,
weight_search: "".to_string(),
param_search: "".to_string(),
ignore_revlogs_before_date: "".to_string(),
easy_days_percentages: vec![1.0; 7],
}
@ -395,7 +395,7 @@ impl From<DeckConfSchema11> for DeckConfig {
easy_days_percentages: c.easy_days_percentages,
desired_retention: c.desired_retention,
historical_retention: c.sm2_retention,
weight_search: c.weight_search,
param_search: c.param_search,
other: other_bytes,
},
}
@ -506,7 +506,7 @@ impl From<DeckConfig> for DeckConfSchema11 {
fsrs_params_5: i.fsrs_params_5,
desired_retention: i.desired_retention,
sm2_retention: i.historical_retention,
weight_search: i.weight_search,
param_search: i.param_search,
ignore_revlogs_before_date: i.ignore_revlogs_before_date,
easy_days_percentages: i.easy_days_percentages,
}

View file

@ -21,7 +21,7 @@ use crate::decks::NormalDeck;
use crate::prelude::*;
use crate::scheduler::fsrs::memory_state::UpdateMemoryStateEntry;
use crate::scheduler::fsrs::memory_state::UpdateMemoryStateRequest;
use crate::scheduler::fsrs::weights::ignore_revlogs_before_ms_from_config;
use crate::scheduler::fsrs::params::ignore_revlogs_before_ms_from_config;
use crate::search::JoinSearches;
use crate::search::Negated;
use crate::search::SearchNode;
@ -159,8 +159,8 @@ impl Collection {
configs_after_update.remove(dcid);
}
if req.mode == UpdateDeckConfigsMode::ComputeAllWeights {
self.compute_all_weights(&mut req)?;
if req.mode == UpdateDeckConfigsMode::ComputeAllParams {
self.compute_all_params(&mut req)?;
}
// add/update provided configs
@ -207,13 +207,13 @@ impl Collection {
if let Ok(normal) = deck.normal() {
let deck_id = deck.id;
// previous order & weights
// previous order & params
let previous_config_id = DeckConfigId(normal.config_id);
let previous_config = configs_before_update.get(&previous_config_id);
let previous_order = previous_config
.map(|c| c.inner.new_card_insert_order())
.unwrap_or_default();
let previous_weights = previous_config.map(|c| c.fsrs_params());
let previous_params = previous_config.map(|c| c.fsrs_params());
let previous_retention = previous_config.map(|c| c.inner.desired_retention);
// if a selected (sub)deck, or its old config was removed, update deck to point
@ -239,11 +239,11 @@ impl Collection {
self.sort_deck(deck_id, current_order, usn)?;
}
// if weights differ, memory state needs to be recomputed
let current_weights = current_config.map(|c| c.fsrs_params());
// if params differ, memory state needs to be recomputed
let current_params = current_config.map(|c| c.fsrs_params());
let current_retention = current_config.map(|c| c.inner.desired_retention);
if fsrs_toggled
|| previous_weights != current_weights
|| previous_params != current_params
|| previous_retention != current_retention
{
decks_needing_memory_recompute
@ -261,10 +261,10 @@ impl Collection {
.into_iter()
.map(|(conf_id, search)| {
let config = configs_after_update.get(&conf_id);
let weights = config.and_then(|c| {
let params = config.and_then(|c| {
if req.fsrs {
Some(UpdateMemoryStateRequest {
weights: c.fsrs_params().clone(),
params: c.fsrs_params().clone(),
desired_retention: c.inner.desired_retention,
max_interval: c.inner.maximum_review_interval,
reschedule: req.fsrs_reschedule,
@ -275,7 +275,7 @@ impl Collection {
}
});
Ok(UpdateMemoryStateEntry {
req: weights,
req: params,
search: SearchNode::DeckIdsWithoutChildren(comma_separated_ids(&search)),
ignore_before: config
.map(ignore_revlogs_before_ms_from_config)
@ -329,7 +329,7 @@ impl Collection {
}
Ok(())
}
fn compute_all_weights(&mut self, req: &mut UpdateDeckConfigsRequest) -> Result<()> {
fn compute_all_params(&mut self, req: &mut UpdateDeckConfigsRequest) -> Result<()> {
require!(req.fsrs, "FSRS must be enabled");
// frontend didn't include any unmodified deck configs, so we need to fill them
@ -344,28 +344,28 @@ impl Collection {
// other parts of the code expect the currently-selected preset to come last
req.configs.push(previous_last);
// calculate and apply weights to each preset
// calculate and apply params to each preset
let config_len = req.configs.len() as u32;
for (idx, config) in req.configs.iter_mut().enumerate() {
let search = if config.inner.weight_search.trim().is_empty() {
let search = if config.inner.param_search.trim().is_empty() {
SearchNode::Preset(config.name.clone())
.and(SearchNode::State(StateKind::Suspended).negated())
.try_into_search()?
.to_string()
} else {
config.inner.weight_search.clone()
config.inner.param_search.clone()
};
let ignore_revlogs_before_ms = ignore_revlogs_before_ms_from_config(config)?;
match self.compute_weights(
match self.compute_params(
&search,
ignore_revlogs_before_ms,
idx as u32 + 1,
config_len,
config.fsrs_params(),
) {
Ok(weights) => {
println!("{}: {:?}", config.name, weights.weights);
config.inner.fsrs_params_5 = weights.weights;
Ok(params) => {
println!("{}: {:?}", config.name, params.params);
config.inner.fsrs_params_5 = params.params;
}
Err(AnkiError::Interrupted) => return Err(AnkiError::Interrupted),
Err(err) => {

View file

@ -113,7 +113,7 @@ pub enum AnkiError {
},
InvalidMethodIndex,
InvalidServiceIndex,
FsrsWeightsInvalid,
FsrsParamsInvalid,
/// Returned by fsrs-rs; may happen even if 400+ reviews
FsrsInsufficientData,
/// Generated by our backend if count < 400
@ -181,7 +181,7 @@ impl AnkiError {
AnkiError::FsrsInsufficientReviews { count } => {
tr.deck_config_must_have_400_reviews(*count).into()
}
AnkiError::FsrsWeightsInvalid => tr.deck_config_invalid_parameters().into(),
AnkiError::FsrsParamsInvalid => tr.deck_config_invalid_parameters().into(),
AnkiError::SchedulerUpgradeRequired => {
tr.scheduling_update_required().replace("V2", "v3")
}

View file

@ -15,8 +15,8 @@ use crate::import_export::ExportProgress;
use crate::import_export::ImportProgress;
use crate::prelude::Collection;
use crate::scheduler::fsrs::memory_state::ComputeMemoryProgress;
use crate::scheduler::fsrs::params::ComputeParamsProgress;
use crate::scheduler::fsrs::retention::ComputeRetentionProgress;
use crate::scheduler::fsrs::weights::ComputeWeightsProgress;
use crate::sync::collection::normal::NormalSyncProgress;
use crate::sync::collection::progress::FullSyncProgress;
use crate::sync::collection::progress::SyncStage;
@ -131,7 +131,7 @@ pub enum Progress {
DatabaseCheck(DatabaseCheckProgress),
Import(ImportProgress),
Export(ExportProgress),
ComputeWeights(ComputeWeightsProgress),
ComputeParams(ComputeParamsProgress),
ComputeRetention(ComputeRetentionProgress),
ComputeMemory(ComputeMemoryProgress),
}
@ -209,8 +209,8 @@ pub(crate) fn progress_to_proto(
}
.into(),
),
Progress::ComputeWeights(progress) => {
Value::ComputeWeights(anki_proto::collection::ComputeWeightsProgress {
Progress::ComputeParams(progress) => {
Value::ComputeParams(anki_proto::collection::ComputeParamsProgress {
current: progress.current_iteration,
total: progress.total_iterations,
reviews: progress.reviews,
@ -296,9 +296,9 @@ impl From<ExportProgress> for Progress {
}
}
impl From<ComputeWeightsProgress> for Progress {
fn from(p: ComputeWeightsProgress) -> Self {
Progress::ComputeWeights(p)
impl From<ComputeParamsProgress> for Progress {
fn from(p: ComputeParamsProgress) -> Self {
Progress::ComputeParams(p)
}
}

View file

@ -14,7 +14,7 @@ use rand::prelude::*;
use rand::rngs::StdRng;
use revlog::RevlogEntryPartial;
use super::fsrs::weights::ignore_revlogs_before_ms_from_config;
use super::fsrs::params::ignore_revlogs_before_ms_from_config;
use super::queue::BuryMode;
use super::states::load_balancer::LoadBalancerContext;
use super::states::steps::LearningSteps;
@ -433,7 +433,7 @@ impl Collection {
let fsrs_next_states = if fsrs_enabled {
let fsrs = FSRS::new(Some(config.fsrs_params()))?;
if card.memory_state.is_none() && card.ctype != CardType::New {
// Card has been moved or imported into an FSRS deck after weights were set,
// Card has been moved or imported into an FSRS deck after params were set,
// and will need its initial memory state to be calculated based on review
// history.
let revlog = self.revlog_for_srs(SearchNode::CardIds(card.id.to_string()))?;

View file

@ -12,10 +12,10 @@ impl From<FSRSError> for AnkiError {
FSRSError::NotEnoughData => AnkiError::FsrsInsufficientData,
FSRSError::OptimalNotFound => AnkiError::FsrsUnableToDetermineDesiredRetention,
FSRSError::Interrupted => AnkiError::Interrupted,
FSRSError::InvalidParameters => AnkiError::FsrsWeightsInvalid,
FSRSError::InvalidParameters => AnkiError::FsrsParamsInvalid,
FSRSError::InvalidInput => AnkiError::InvalidInput {
source: InvalidInputError {
message: "invalid weights provided".to_string(),
message: "invalid params provided".to_string(),
source: None,
backtrace: None,
},

View file

@ -9,13 +9,13 @@ use fsrs::MemoryState;
use fsrs::FSRS;
use itertools::Itertools;
use super::weights::ignore_revlogs_before_ms_from_config;
use super::params::ignore_revlogs_before_ms_from_config;
use crate::card::CardType;
use crate::prelude::*;
use crate::revlog::RevlogEntry;
use crate::revlog::RevlogReviewKind;
use crate::scheduler::fsrs::weights::single_card_revlog_to_items;
use crate::scheduler::fsrs::weights::Weights;
use crate::scheduler::fsrs::params::single_card_revlog_to_items;
use crate::scheduler::fsrs::params::Params;
use crate::scheduler::states::fuzz::with_review_fuzz;
use crate::search::Negated;
use crate::search::SearchNode;
@ -29,7 +29,7 @@ pub struct ComputeMemoryProgress {
#[derive(Debug)]
pub(crate) struct UpdateMemoryStateRequest {
pub weights: Weights,
pub params: Params,
pub desired_retention: f32,
pub historical_retention: f32,
pub max_interval: u32,
@ -43,10 +43,10 @@ pub(crate) struct UpdateMemoryStateEntry {
}
impl Collection {
/// For each provided set of weights, locate cards with the provided search,
/// For each provided set of params, locate cards with the provided search,
/// and update their memory state.
/// Should be called inside a transaction.
/// If Weights are None, it means the user disabled FSRS, and the existing
/// If Params are None, it means the user disabled FSRS, and the existing
/// memory state should be removed.
pub(crate) fn update_memory_state(
&mut self,
@ -69,7 +69,7 @@ impl Collection {
} else {
None
};
let fsrs = FSRS::new(req.as_ref().map(|w| &w.weights[..]).or(Some([].as_slice())))?;
let fsrs = FSRS::new(req.as_ref().map(|w| &w.params[..]).or(Some([].as_slice())))?;
let historical_retention = req.as_ref().map(|w| w.historical_retention);
let items = fsrs_items_for_memory_state(
&fsrs,
@ -337,8 +337,8 @@ mod tests {
use super::*;
use crate::card::FsrsMemoryState;
use crate::revlog::RevlogReviewKind;
use crate::scheduler::fsrs::weights::tests::convert;
use crate::scheduler::fsrs::weights::tests::revlog;
use crate::scheduler::fsrs::params::tests::convert;
use crate::scheduler::fsrs::params::tests::revlog;
/// Floating point precision can vary between platforms, and each FSRS
/// update tends to result in small changes to these numbers, so we

View file

@ -2,7 +2,7 @@
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
mod error;
pub mod memory_state;
pub mod params;
pub mod retention;
pub mod simulator;
pub mod try_collect;
pub mod weights;

View file

@ -7,7 +7,7 @@ use std::thread;
use std::time::Duration;
use anki_io::write_file;
use anki_proto::scheduler::ComputeFsrsWeightsResponse;
use anki_proto::scheduler::ComputeFsrsParamsResponse;
use anki_proto::stats::revlog_entry;
use anki_proto::stats::Dataset;
use anki_proto::stats::DeckEntry;
@ -29,7 +29,7 @@ use crate::search::Node;
use crate::search::SearchNode;
use crate::search::SortMode;
pub(crate) type Weights = Vec<f32>;
pub(crate) type Params = Vec<f32>;
fn ignore_revlogs_before_date_to_ms(
ignore_revlogs_before_date: &String,
@ -53,15 +53,15 @@ impl Collection {
/// Note this does not return an error if there are less than 400 items -
/// the caller should instead check the fsrs_items count in the return
/// value.
pub fn compute_weights(
pub fn compute_params(
&mut self,
search: &str,
ignore_revlogs_before: TimestampMillis,
current_preset: u32,
total_presets: u32,
current_weights: &Weights,
) -> Result<ComputeFsrsWeightsResponse> {
let mut anki_progress = self.new_progress_handler::<ComputeWeightsProgress>();
current_params: &Params,
) -> Result<ComputeFsrsParamsResponse> {
let mut anki_progress = self.new_progress_handler::<ComputeParamsProgress>();
let timing = self.timing_today()?;
let revlogs = self.revlog_for_srs(search)?;
let (items, review_count) =
@ -69,8 +69,8 @@ impl Collection {
let fsrs_items = items.len() as u32;
if fsrs_items == 0 {
return Ok(ComputeFsrsWeightsResponse {
weights: current_weights.to_vec(),
return Ok(ComputeFsrsParamsResponse {
params: current_params.to_vec(),
fsrs_items,
});
}
@ -97,20 +97,17 @@ impl Collection {
}
}
});
let mut weights = FSRS::new(None)?.compute_parameters(items.clone(), Some(progress2))?;
if let Ok(fsrs) = FSRS::new(Some(current_weights)) {
let mut params = FSRS::new(None)?.compute_parameters(items.clone(), Some(progress2))?;
if let Ok(fsrs) = FSRS::new(Some(current_params)) {
let current_rmse = fsrs.evaluate(items.clone(), |_| true)?.rmse_bins;
let optimized_fsrs = FSRS::new(Some(&weights))?;
let optimized_fsrs = FSRS::new(Some(&params))?;
let optimized_rmse = optimized_fsrs.evaluate(items.clone(), |_| true)?.rmse_bins;
if current_rmse <= optimized_rmse {
weights = current_weights.to_vec();
params = current_params.to_vec();
}
}
Ok(ComputeFsrsWeightsResponse {
weights,
fsrs_items,
})
Ok(ComputeFsrsParamsResponse { params, fsrs_items })
}
pub(crate) fn revlog_for_srs(
@ -180,14 +177,14 @@ impl Collection {
Ok(())
}
pub fn evaluate_weights(
pub fn evaluate_params(
&mut self,
weights: &Weights,
params: &Params,
search: &str,
ignore_revlogs_before: TimestampMillis,
) -> Result<ModelEvaluation> {
let timing = self.timing_today()?;
let mut anki_progress = self.new_progress_handler::<ComputeWeightsProgress>();
let mut anki_progress = self.new_progress_handler::<ComputeParamsProgress>();
let guard = self.search_cards_into_table(search, SortMode::NoOrder)?;
let revlogs: Vec<RevlogEntry> = guard
.col
@ -196,7 +193,7 @@ impl Collection {
let (items, review_count) =
fsrs_items_for_training(revlogs, timing.next_day_at, ignore_revlogs_before);
anki_progress.state.reviews = review_count as u32;
let fsrs = FSRS::new(Some(weights))?;
let fsrs = FSRS::new(Some(params))?;
Ok(fsrs.evaluate(items, |ip| {
anki_progress
.update(false, |p| {
@ -209,13 +206,13 @@ impl Collection {
}
#[derive(Default, Clone, Copy, Debug)]
pub struct ComputeWeightsProgress {
pub struct ComputeParamsProgress {
pub current_iteration: u32,
pub total_iterations: u32,
pub reviews: u32,
/// Only used in 'compute all weights' case
/// Only used in 'compute all params' case
pub current_preset: u32,
/// Only used in 'compute all weights' case
/// Only used in 'compute all params' case
pub total_presets: u32,
}

View file

@ -54,7 +54,7 @@ impl Collection {
learn_limit,
review_limit: usize::MAX,
},
&req.weights,
&req.params,
|ip| {
anki_progress
.update(false, |p| {

View file

@ -54,7 +54,7 @@ impl Collection {
daily_time_cost,
) = simulate(
&config,
&req.weights,
&req.params,
req.desired_retention,
None,
Some(converted_cards),

View file

@ -7,7 +7,7 @@ mod states;
use anki_proto::cards;
use anki_proto::generic;
use anki_proto::scheduler;
use anki_proto::scheduler::ComputeFsrsWeightsResponse;
use anki_proto::scheduler::ComputeFsrsParamsResponse;
use anki_proto::scheduler::ComputeMemoryStateResponse;
use anki_proto::scheduler::ComputeOptimalRetentionRequest;
use anki_proto::scheduler::ComputeOptimalRetentionResponse;
@ -254,16 +254,16 @@ impl crate::services::SchedulerService for Collection {
self.custom_study_defaults(input.deck_id.into())
}
fn compute_fsrs_weights(
fn compute_fsrs_params(
&mut self,
input: scheduler::ComputeFsrsWeightsRequest,
) -> Result<scheduler::ComputeFsrsWeightsResponse> {
self.compute_weights(
input: scheduler::ComputeFsrsParamsRequest,
) -> Result<scheduler::ComputeFsrsParamsResponse> {
self.compute_params(
&input.search,
input.ignore_revlogs_before_ms.into(),
1,
1,
&input.current_weights,
&input.current_params,
)
}
@ -283,16 +283,16 @@ impl crate::services::SchedulerService for Collection {
})
}
fn evaluate_weights(
fn evaluate_params(
&mut self,
input: scheduler::EvaluateWeightsRequest,
) -> Result<scheduler::EvaluateWeightsResponse> {
let ret = self.evaluate_weights(
&input.weights,
input: scheduler::EvaluateParamsRequest,
) -> Result<scheduler::EvaluateParamsResponse> {
let ret = self.evaluate_params(
&input.params,
&input.search,
input.ignore_revlogs_before_ms.into(),
)?;
Ok(scheduler::EvaluateWeightsResponse {
Ok(scheduler::EvaluateParamsResponse {
log_loss: ret.log_loss,
rmse_bins: ret.rmse_bins,
})
@ -339,20 +339,17 @@ impl crate::services::SchedulerService for Collection {
}
impl crate::services::BackendSchedulerService for Backend {
fn compute_fsrs_weights_from_items(
fn compute_fsrs_params_from_items(
&self,
req: scheduler::ComputeFsrsWeightsFromItemsRequest,
) -> Result<scheduler::ComputeFsrsWeightsResponse> {
req: scheduler::ComputeFsrsParamsFromItemsRequest,
) -> Result<scheduler::ComputeFsrsParamsResponse> {
let fsrs = FSRS::new(None)?;
let fsrs_items = req.items.len() as u32;
let weights = fsrs.compute_parameters(
let params = fsrs.compute_parameters(
req.items.into_iter().map(fsrs_item_proto_to_fsrs).collect(),
None,
)?;
Ok(ComputeFsrsWeightsResponse {
weights,
fsrs_items,
})
Ok(ComputeFsrsParamsResponse { params, fsrs_items })
}
fn fsrs_benchmark(
@ -365,8 +362,8 @@ impl crate::services::BackendSchedulerService for Backend {
.into_iter()
.map(fsrs_item_proto_to_fsrs)
.collect();
let weights = fsrs.benchmark(train_set);
Ok(FsrsBenchmarkResponse { weights })
let params = fsrs.benchmark(train_set);
Ok(FsrsBenchmarkResponse { params })
}
fn export_dataset(&self, req: scheduler::ExportDatasetRequest) -> Result<()> {

View file

@ -226,8 +226,8 @@ impl LoadBalancer {
.collect::<Vec<_>>();
let expected_distribution = check_review_distribution(&review_counts, &percentages);
// calculate weights for each day
let intervals_and_weights = interval_days
// calculate params for each day
let intervals_and_params = interval_days
.iter()
.enumerate()
.map(|(interval_index, interval_day)| {
@ -262,10 +262,10 @@ impl LoadBalancer {
let mut rng = StdRng::seed_from_u64(fuzz_seed?);
let weighted_intervals =
WeightedIndex::new(intervals_and_weights.iter().map(|k| k.1)).ok()?;
WeightedIndex::new(intervals_and_params.iter().map(|k| k.1)).ok()?;
let selected_interval_index = weighted_intervals.sample(&mut rng);
Some(intervals_and_weights[selected_interval_index].0)
Some(intervals_and_params[selected_interval_index].0)
}
pub fn add_card(&mut self, cid: CardId, nid: NoteId, dcid: DeckConfigId, interval: u32) {

View file

@ -7,7 +7,7 @@ use crate::card::CardType;
use crate::prelude::*;
use crate::revlog::RevlogEntry;
use crate::scheduler::fsrs::memory_state::single_card_revlog_to_item;
use crate::scheduler::fsrs::weights::ignore_revlogs_before_ms_from_config;
use crate::scheduler::fsrs::params::ignore_revlogs_before_ms_from_config;
use crate::scheduler::timing::is_unix_epoch_timestamp;
impl Collection {

View file

@ -5,7 +5,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script lang="ts">
import {
ComputeRetentionProgress,
type ComputeWeightsProgress,
type ComputeParamsProgress,
} from "@generated/anki/collection_pb";
import {
ComputeOptimalRetentionRequest,
@ -13,10 +13,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
type SimulateFsrsReviewResponse,
} from "@generated/anki/scheduler_pb";
import {
computeFsrsWeights,
computeFsrsParams,
computeOptimalRetention,
simulateFsrsReview,
evaluateWeights,
evaluateParams,
setWantsAbort,
} from "@generated/backend";
import * as tr from "@generated/ftl";
@ -30,8 +30,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import SpinBoxFloatRow from "./SpinBoxFloatRow.svelte";
import SpinBoxRow from "./SpinBoxRow.svelte";
import Warning from "./Warning.svelte";
import WeightsInputRow from "./WeightsInputRow.svelte";
import WeightsSearchRow from "./WeightsSearchRow.svelte";
import ParamsInputRow from "./ParamsInputRow.svelte";
import ParamsSearchRow from "./ParamsSearchRow.svelte";
import { renderSimulationChart, type Point } from "../graphs/simulator";
import Graph from "../graphs/Graph.svelte";
import HoverColumns from "../graphs/HoverColumns.svelte";
@ -54,22 +54,22 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
$: lastOptimizationWarning =
$daysSinceLastOptimization > 30 ? tr.deckConfigOptimizeAllTip() : "";
let computeWeightsProgress: ComputeWeightsProgress | undefined;
let computingWeights = false;
let checkingWeights = false;
let computeParamsProgress: ComputeParamsProgress | undefined;
let computingParams = false;
let checkingParams = false;
let computingRetention = false;
let optimalRetention = 0;
$: if ($presetName) {
optimalRetention = 0;
}
$: computing = computingWeights || checkingWeights || computingRetention;
$: defaultWeightSearch = `preset:"${state.getCurrentName()}" -is:suspended`;
$: computing = computingParams || checkingParams || computingRetention;
$: defaultparamSearch = `preset:"${state.getCurrentName()}" -is:suspended`;
$: roundedRetention = Number($config.desiredRetention.toFixed(2));
$: desiredRetentionWarning = getRetentionWarning(roundedRetention);
$: retentionWarningClass = getRetentionWarningClass(roundedRetention);
let computeRetentionProgress:
| ComputeWeightsProgress
| ComputeParamsProgress
| ComputeRetentionProgress
| undefined;
@ -82,7 +82,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
const simulateFsrsRequest = new SimulateFsrsReviewRequest({
weights: fsrsParams($config),
params: fsrsParams($config),
desiredRetention: $config.desiredRetention,
deckSize: 0,
daysToSimulate: 365,
@ -123,8 +123,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
);
}
async function computeWeights(): Promise<void> {
if (computingWeights) {
async function computeParams(): Promise<void> {
if (computingParams) {
await setWantsAbort({});
return;
}
@ -132,47 +132,47 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
alert(tr.deckConfigPleaseSaveYourChangesFirst());
return;
}
computingWeights = true;
computeWeightsProgress = undefined;
computingParams = true;
computeParamsProgress = undefined;
try {
await runWithBackendProgress(
async () => {
const params = fsrsParams($config);
const resp = await computeFsrsWeights({
search: $config.weightSearch
? $config.weightSearch
: defaultWeightSearch,
const resp = await computeFsrsParams({
search: $config.paramSearch
? $config.paramSearch
: defaultparamSearch,
ignoreRevlogsBeforeMs: getIgnoreRevlogsBeforeMs(),
currentWeights: params,
currentParams: params,
});
if (
(params.length &&
params.every(
(n, i) => n.toFixed(4) === resp.weights[i].toFixed(4),
(n, i) => n.toFixed(4) === resp.params[i].toFixed(4),
)) ||
resp.weights.length === 0
resp.params.length === 0
) {
setTimeout(() => alert(tr.deckConfigFsrsParamsOptimal()), 100);
} else {
$config.fsrsParams5 = resp.weights;
$config.fsrsParams5 = resp.params;
}
if (computeWeightsProgress) {
computeWeightsProgress.current = computeWeightsProgress.total;
if (computeParamsProgress) {
computeParamsProgress.current = computeParamsProgress.total;
}
},
(progress) => {
if (progress.value.case === "computeWeights") {
computeWeightsProgress = progress.value.value;
if (progress.value.case === "computeParams") {
computeParamsProgress = progress.value.value;
}
},
);
} finally {
computingWeights = false;
computingParams = false;
}
}
async function checkWeights(): Promise<void> {
if (checkingWeights) {
async function checkParams(): Promise<void> {
if (checkingParams) {
await setWantsAbort({});
return;
}
@ -180,21 +180,21 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
alert(tr.deckConfigPleaseSaveYourChangesFirst());
return;
}
checkingWeights = true;
computeWeightsProgress = undefined;
checkingParams = true;
computeParamsProgress = undefined;
try {
await runWithBackendProgress(
async () => {
const search = $config.weightSearch
? $config.weightSearch
: defaultWeightSearch;
const resp = await evaluateWeights({
weights: fsrsParams($config),
const search = $config.paramSearch
? $config.paramSearch
: defaultparamSearch;
const resp = await evaluateParams({
params: fsrsParams($config),
search,
ignoreRevlogsBeforeMs: getIgnoreRevlogsBeforeMs(),
});
if (computeWeightsProgress) {
computeWeightsProgress.current = computeWeightsProgress.total;
if (computeParamsProgress) {
computeParamsProgress.current = computeParamsProgress.total;
}
setTimeout(
() =>
@ -207,13 +207,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
);
},
(progress) => {
if (progress.value.case === "computeWeights") {
computeWeightsProgress = progress.value.value;
if (progress.value.case === "computeParams") {
computeParamsProgress = progress.value.value;
}
},
);
} finally {
checkingWeights = false;
checkingParams = false;
}
}
@ -232,7 +232,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
await runWithBackendProgress(
async () => {
optimalRetentionRequest.maxInterval = $config.maximumReviewInterval;
optimalRetentionRequest.weights = fsrsParams($config);
optimalRetentionRequest.params = fsrsParams($config);
optimalRetentionRequest.search = `preset:"${state.getCurrentName()}" -is:suspended`;
const resp = await computeOptimalRetention(optimalRetentionRequest);
optimalRetention = resp.optimalRetention;
@ -249,13 +249,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
}
$: computeWeightsProgressString = renderWeightProgress(computeWeightsProgress);
$: computeParamsProgressString = renderWeightProgress(computeParamsProgress);
$: computeRetentionProgressString = renderRetentionProgress(
computeRetentionProgress,
);
$: totalReviews = computeWeightsProgress?.reviews ?? undefined;
$: totalReviews = computeParamsProgress?.reviews ?? undefined;
function renderWeightProgress(val: ComputeWeightsProgress | undefined): String {
function renderWeightProgress(val: ComputeParamsProgress | undefined): String {
if (!val || !val.total) {
return "";
}
@ -313,7 +313,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
try {
await runWithBackendProgress(
async () => {
simulateFsrsRequest.weights = fsrsParams($config);
simulateFsrsRequest.params = fsrsParams($config);
simulateFsrsRequest.desiredRetention = $config.desiredRetention;
simulateFsrsRequest.search = `preset:"${state.getCurrentName()}" -is:suspended`;
simulateProgressString = "processing...";
@ -361,46 +361,46 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<Warning warning={desiredRetentionWarning} className={retentionWarningClass} />
<div class="ms-1 me-1">
<WeightsInputRow
<ParamsInputRow
bind:value={$config.fsrsParams5}
defaultValue={[]}
defaults={defaults.fsrsParams5}
>
<SettingTitle on:click={() => openHelpModal("modelWeights")}>
<SettingTitle on:click={() => openHelpModal("modelParams")}>
{tr.deckConfigWeights()}
</SettingTitle>
</WeightsInputRow>
</ParamsInputRow>
<WeightsSearchRow
bind:value={$config.weightSearch}
placeholder={defaultWeightSearch}
<ParamsSearchRow
bind:value={$config.paramSearch}
placeholder={defaultparamSearch}
/>
<button
class="btn {computingWeights ? 'btn-warning' : 'btn-primary'}"
disabled={!computingWeights && computing}
on:click={() => computeWeights()}
class="btn {computingParams ? 'btn-warning' : 'btn-primary'}"
disabled={!computingParams && computing}
on:click={() => computeParams()}
>
{#if computingWeights}
{#if computingParams}
{tr.actionsCancel()}
{:else}
{tr.deckConfigOptimizeButton()}
{/if}
</button>
<button
class="btn {checkingWeights ? 'btn-warning' : 'btn-primary'}"
disabled={!checkingWeights && computing}
on:click={() => checkWeights()}
class="btn {checkingParams ? 'btn-warning' : 'btn-primary'}"
disabled={!checkingParams && computing}
on:click={() => checkParams()}
>
{#if checkingWeights}
{#if checkingParams}
{tr.actionsCancel()}
{:else}
{tr.deckConfigEvaluateButton()}
{/if}
</button>
<div>
{#if computingWeights || checkingWeights}
{computeWeightsProgressString}
{#if computingParams || checkingParams}
{computeParamsProgressString}
{:else if totalReviews !== undefined}
{tr.statisticsReviews({ reviews: totalReviews })}
{/if}

View file

@ -37,7 +37,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
help: tr.deckConfigDesiredRetentionTooltip(),
sched: HelpItemScheduler.FSRS,
},
modelWeights: {
modelParams: {
title: tr.deckConfigWeights(),
help:
tr.deckConfigWeightsTooltip2() +

View file

@ -9,8 +9,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
let stringValue: string;
$: stringValue = render(value);
function render(weights: number[]): string {
return weights.map((v) => v.toFixed(4)).join(", ");
function render(params: number[]): string {
return params.map((v) => v.toFixed(4)).join(", ");
}
function update(this: HTMLInputElement): void {

View file

@ -6,7 +6,7 @@
import ConfigInput from "$lib/components/ConfigInput.svelte";
import RevertButton from "$lib/components/RevertButton.svelte";
import WeightsInput from "./WeightsInput.svelte";
import ParamsInput from "./ParamsInput.svelte";
export let value: number[];
export let defaultValue: number[];
@ -15,6 +15,6 @@
<slot />
<ConfigInput>
<WeightsInput bind:value {defaults} />
<ParamsInput bind:value {defaults} />
<RevertButton slot="revert" bind:value {defaultValue} />
</ConfigInput>

View file

@ -56,7 +56,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
async function save(mode: UpdateDeckConfigsMode): Promise<void> {
await commitEditing();
if (mode === UpdateDeckConfigsMode.COMPUTE_ALL_WEIGHTS && !get(state.fsrs)) {
if (mode === UpdateDeckConfigsMode.COMPUTE_ALL_PARAMS && !get(state.fsrs)) {
alert(tr.deckConfigFsrsMustBeEnabled());
return;
}
@ -119,7 +119,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{tr.deckConfigSaveToAllSubdecks()}
</DropdownItem>
<DropdownItem
on:click={() => save(UpdateDeckConfigsMode.COMPUTE_ALL_WEIGHTS)}
on:click={() => save(UpdateDeckConfigsMode.COMPUTE_ALL_PARAMS)}
>
{tr.deckConfigSaveAndOptimize()}
</DropdownItem>