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

View file

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

View file

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

View file

@ -149,8 +149,8 @@ class RustBackend(RustBackendGenerated):
) )
return self.format_timespan(seconds=seconds, context=context) return self.format_timespan(seconds=seconds, context=context)
def compute_weights_from_items(self, items: Iterable[FsrsItem]) -> Sequence[float]: def compute_params_from_items(self, items: Iterable[FsrsItem]) -> Sequence[float]:
return self.compute_fsrs_weights_from_items(items).weights return self.compute_fsrs_params_from_items(items).params
def benchmark(self, train_set: Iterable[FsrsItem]) -> Sequence[float]: def benchmark(self, train_set: Iterable[FsrsItem]) -> Sequence[float]:
return self.fsrs_benchmark(train_set=train_set) 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.max = val.total_cards
update.value = val.current_cards update.value = val.current_cards
update.label = val.label update.label = val.label
elif progress.HasField("compute_weights"): elif progress.HasField("compute_params"):
val2 = progress.compute_weights val2 = progress.compute_params
# prevent an indeterminate progress bar from appearing at the start of each preset # prevent an indeterminate progress bar from appearing at the start of each preset
update.max = max(val2.total, 1) update.max = max(val2.total, 1)
update.value = val2.current update.value = val2.current
@ -621,10 +621,10 @@ exposed_backend_list = [
"update_image_occlusion_note", "update_image_occlusion_note",
"get_image_occlusion_fields", "get_image_occlusion_fields",
# SchedulerService # SchedulerService
"compute_fsrs_weights", "compute_fsrs_params",
"compute_optimal_retention", "compute_optimal_retention",
"set_wants_abort", "set_wants_abort",
"evaluate_weights", "evaluate_params",
"get_optimal_retention_parameters", "get_optimal_retention_parameters",
"simulate_fsrs_review", "simulate_fsrs_review",
] ]

View file

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

View file

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

View file

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

View file

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

View file

@ -113,7 +113,7 @@ pub enum AnkiError {
}, },
InvalidMethodIndex, InvalidMethodIndex,
InvalidServiceIndex, InvalidServiceIndex,
FsrsWeightsInvalid, FsrsParamsInvalid,
/// Returned by fsrs-rs; may happen even if 400+ reviews /// Returned by fsrs-rs; may happen even if 400+ reviews
FsrsInsufficientData, FsrsInsufficientData,
/// Generated by our backend if count < 400 /// Generated by our backend if count < 400
@ -181,7 +181,7 @@ impl AnkiError {
AnkiError::FsrsInsufficientReviews { count } => { AnkiError::FsrsInsufficientReviews { count } => {
tr.deck_config_must_have_400_reviews(*count).into() 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 => { AnkiError::SchedulerUpgradeRequired => {
tr.scheduling_update_required().replace("V2", "v3") 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::import_export::ImportProgress;
use crate::prelude::Collection; use crate::prelude::Collection;
use crate::scheduler::fsrs::memory_state::ComputeMemoryProgress; use crate::scheduler::fsrs::memory_state::ComputeMemoryProgress;
use crate::scheduler::fsrs::params::ComputeParamsProgress;
use crate::scheduler::fsrs::retention::ComputeRetentionProgress; use crate::scheduler::fsrs::retention::ComputeRetentionProgress;
use crate::scheduler::fsrs::weights::ComputeWeightsProgress;
use crate::sync::collection::normal::NormalSyncProgress; use crate::sync::collection::normal::NormalSyncProgress;
use crate::sync::collection::progress::FullSyncProgress; use crate::sync::collection::progress::FullSyncProgress;
use crate::sync::collection::progress::SyncStage; use crate::sync::collection::progress::SyncStage;
@ -131,7 +131,7 @@ pub enum Progress {
DatabaseCheck(DatabaseCheckProgress), DatabaseCheck(DatabaseCheckProgress),
Import(ImportProgress), Import(ImportProgress),
Export(ExportProgress), Export(ExportProgress),
ComputeWeights(ComputeWeightsProgress), ComputeParams(ComputeParamsProgress),
ComputeRetention(ComputeRetentionProgress), ComputeRetention(ComputeRetentionProgress),
ComputeMemory(ComputeMemoryProgress), ComputeMemory(ComputeMemoryProgress),
} }
@ -209,8 +209,8 @@ pub(crate) fn progress_to_proto(
} }
.into(), .into(),
), ),
Progress::ComputeWeights(progress) => { Progress::ComputeParams(progress) => {
Value::ComputeWeights(anki_proto::collection::ComputeWeightsProgress { Value::ComputeParams(anki_proto::collection::ComputeParamsProgress {
current: progress.current_iteration, current: progress.current_iteration,
total: progress.total_iterations, total: progress.total_iterations,
reviews: progress.reviews, reviews: progress.reviews,
@ -296,9 +296,9 @@ impl From<ExportProgress> for Progress {
} }
} }
impl From<ComputeWeightsProgress> for Progress { impl From<ComputeParamsProgress> for Progress {
fn from(p: ComputeWeightsProgress) -> Self { fn from(p: ComputeParamsProgress) -> Self {
Progress::ComputeWeights(p) Progress::ComputeParams(p)
} }
} }

View file

@ -14,7 +14,7 @@ use rand::prelude::*;
use rand::rngs::StdRng; use rand::rngs::StdRng;
use revlog::RevlogEntryPartial; 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::queue::BuryMode;
use super::states::load_balancer::LoadBalancerContext; use super::states::load_balancer::LoadBalancerContext;
use super::states::steps::LearningSteps; use super::states::steps::LearningSteps;
@ -433,7 +433,7 @@ impl Collection {
let fsrs_next_states = if fsrs_enabled { let fsrs_next_states = if fsrs_enabled {
let fsrs = FSRS::new(Some(config.fsrs_params()))?; let fsrs = FSRS::new(Some(config.fsrs_params()))?;
if card.memory_state.is_none() && card.ctype != CardType::New { 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 // and will need its initial memory state to be calculated based on review
// history. // history.
let revlog = self.revlog_for_srs(SearchNode::CardIds(card.id.to_string()))?; 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::NotEnoughData => AnkiError::FsrsInsufficientData,
FSRSError::OptimalNotFound => AnkiError::FsrsUnableToDetermineDesiredRetention, FSRSError::OptimalNotFound => AnkiError::FsrsUnableToDetermineDesiredRetention,
FSRSError::Interrupted => AnkiError::Interrupted, FSRSError::Interrupted => AnkiError::Interrupted,
FSRSError::InvalidParameters => AnkiError::FsrsWeightsInvalid, FSRSError::InvalidParameters => AnkiError::FsrsParamsInvalid,
FSRSError::InvalidInput => AnkiError::InvalidInput { FSRSError::InvalidInput => AnkiError::InvalidInput {
source: InvalidInputError { source: InvalidInputError {
message: "invalid weights provided".to_string(), message: "invalid params provided".to_string(),
source: None, source: None,
backtrace: None, backtrace: None,
}, },

View file

@ -9,13 +9,13 @@ use fsrs::MemoryState;
use fsrs::FSRS; use fsrs::FSRS;
use itertools::Itertools; 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::card::CardType;
use crate::prelude::*; use crate::prelude::*;
use crate::revlog::RevlogEntry; use crate::revlog::RevlogEntry;
use crate::revlog::RevlogReviewKind; use crate::revlog::RevlogReviewKind;
use crate::scheduler::fsrs::weights::single_card_revlog_to_items; use crate::scheduler::fsrs::params::single_card_revlog_to_items;
use crate::scheduler::fsrs::weights::Weights; use crate::scheduler::fsrs::params::Params;
use crate::scheduler::states::fuzz::with_review_fuzz; use crate::scheduler::states::fuzz::with_review_fuzz;
use crate::search::Negated; use crate::search::Negated;
use crate::search::SearchNode; use crate::search::SearchNode;
@ -29,7 +29,7 @@ pub struct ComputeMemoryProgress {
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct UpdateMemoryStateRequest { pub(crate) struct UpdateMemoryStateRequest {
pub weights: Weights, pub params: Params,
pub desired_retention: f32, pub desired_retention: f32,
pub historical_retention: f32, pub historical_retention: f32,
pub max_interval: u32, pub max_interval: u32,
@ -43,10 +43,10 @@ pub(crate) struct UpdateMemoryStateEntry {
} }
impl Collection { 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. /// and update their memory state.
/// Should be called inside a transaction. /// 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. /// memory state should be removed.
pub(crate) fn update_memory_state( pub(crate) fn update_memory_state(
&mut self, &mut self,
@ -69,7 +69,7 @@ impl Collection {
} else { } else {
None 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 historical_retention = req.as_ref().map(|w| w.historical_retention);
let items = fsrs_items_for_memory_state( let items = fsrs_items_for_memory_state(
&fsrs, &fsrs,
@ -337,8 +337,8 @@ mod tests {
use super::*; use super::*;
use crate::card::FsrsMemoryState; use crate::card::FsrsMemoryState;
use crate::revlog::RevlogReviewKind; use crate::revlog::RevlogReviewKind;
use crate::scheduler::fsrs::weights::tests::convert; use crate::scheduler::fsrs::params::tests::convert;
use crate::scheduler::fsrs::weights::tests::revlog; use crate::scheduler::fsrs::params::tests::revlog;
/// Floating point precision can vary between platforms, and each FSRS /// Floating point precision can vary between platforms, and each FSRS
/// update tends to result in small changes to these numbers, so we /// 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 // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
mod error; mod error;
pub mod memory_state; pub mod memory_state;
pub mod params;
pub mod retention; pub mod retention;
pub mod simulator; pub mod simulator;
pub mod try_collect; pub mod try_collect;
pub mod weights;

View file

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

View file

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

View file

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

View file

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

View file

@ -226,8 +226,8 @@ impl LoadBalancer {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let expected_distribution = check_review_distribution(&review_counts, &percentages); let expected_distribution = check_review_distribution(&review_counts, &percentages);
// calculate weights for each day // calculate params for each day
let intervals_and_weights = interval_days let intervals_and_params = interval_days
.iter() .iter()
.enumerate() .enumerate()
.map(|(interval_index, interval_day)| { .map(|(interval_index, interval_day)| {
@ -262,10 +262,10 @@ impl LoadBalancer {
let mut rng = StdRng::seed_from_u64(fuzz_seed?); let mut rng = StdRng::seed_from_u64(fuzz_seed?);
let weighted_intervals = 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); 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) { 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::prelude::*;
use crate::revlog::RevlogEntry; use crate::revlog::RevlogEntry;
use crate::scheduler::fsrs::memory_state::single_card_revlog_to_item; 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; use crate::scheduler::timing::is_unix_epoch_timestamp;
impl Collection { 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"> <script lang="ts">
import { import {
ComputeRetentionProgress, ComputeRetentionProgress,
type ComputeWeightsProgress, type ComputeParamsProgress,
} from "@generated/anki/collection_pb"; } from "@generated/anki/collection_pb";
import { import {
ComputeOptimalRetentionRequest, ComputeOptimalRetentionRequest,
@ -13,10 +13,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
type SimulateFsrsReviewResponse, type SimulateFsrsReviewResponse,
} from "@generated/anki/scheduler_pb"; } from "@generated/anki/scheduler_pb";
import { import {
computeFsrsWeights, computeFsrsParams,
computeOptimalRetention, computeOptimalRetention,
simulateFsrsReview, simulateFsrsReview,
evaluateWeights, evaluateParams,
setWantsAbort, setWantsAbort,
} from "@generated/backend"; } from "@generated/backend";
import * as tr from "@generated/ftl"; 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 SpinBoxFloatRow from "./SpinBoxFloatRow.svelte";
import SpinBoxRow from "./SpinBoxRow.svelte"; import SpinBoxRow from "./SpinBoxRow.svelte";
import Warning from "./Warning.svelte"; import Warning from "./Warning.svelte";
import WeightsInputRow from "./WeightsInputRow.svelte"; import ParamsInputRow from "./ParamsInputRow.svelte";
import WeightsSearchRow from "./WeightsSearchRow.svelte"; import ParamsSearchRow from "./ParamsSearchRow.svelte";
import { renderSimulationChart, type Point } from "../graphs/simulator"; import { renderSimulationChart, type Point } from "../graphs/simulator";
import Graph from "../graphs/Graph.svelte"; import Graph from "../graphs/Graph.svelte";
import HoverColumns from "../graphs/HoverColumns.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 = $: lastOptimizationWarning =
$daysSinceLastOptimization > 30 ? tr.deckConfigOptimizeAllTip() : ""; $daysSinceLastOptimization > 30 ? tr.deckConfigOptimizeAllTip() : "";
let computeWeightsProgress: ComputeWeightsProgress | undefined; let computeParamsProgress: ComputeParamsProgress | undefined;
let computingWeights = false; let computingParams = false;
let checkingWeights = false; let checkingParams = false;
let computingRetention = false; let computingRetention = false;
let optimalRetention = 0; let optimalRetention = 0;
$: if ($presetName) { $: if ($presetName) {
optimalRetention = 0; optimalRetention = 0;
} }
$: computing = computingWeights || checkingWeights || computingRetention; $: computing = computingParams || checkingParams || computingRetention;
$: defaultWeightSearch = `preset:"${state.getCurrentName()}" -is:suspended`; $: defaultparamSearch = `preset:"${state.getCurrentName()}" -is:suspended`;
$: roundedRetention = Number($config.desiredRetention.toFixed(2)); $: roundedRetention = Number($config.desiredRetention.toFixed(2));
$: desiredRetentionWarning = getRetentionWarning(roundedRetention); $: desiredRetentionWarning = getRetentionWarning(roundedRetention);
$: retentionWarningClass = getRetentionWarningClass(roundedRetention); $: retentionWarningClass = getRetentionWarningClass(roundedRetention);
let computeRetentionProgress: let computeRetentionProgress:
| ComputeWeightsProgress | ComputeParamsProgress
| ComputeRetentionProgress | ComputeRetentionProgress
| undefined; | undefined;
@ -82,7 +82,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
const simulateFsrsRequest = new SimulateFsrsReviewRequest({ const simulateFsrsRequest = new SimulateFsrsReviewRequest({
weights: fsrsParams($config), params: fsrsParams($config),
desiredRetention: $config.desiredRetention, desiredRetention: $config.desiredRetention,
deckSize: 0, deckSize: 0,
daysToSimulate: 365, 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> { async function computeParams(): Promise<void> {
if (computingWeights) { if (computingParams) {
await setWantsAbort({}); await setWantsAbort({});
return; return;
} }
@ -132,47 +132,47 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
alert(tr.deckConfigPleaseSaveYourChangesFirst()); alert(tr.deckConfigPleaseSaveYourChangesFirst());
return; return;
} }
computingWeights = true; computingParams = true;
computeWeightsProgress = undefined; computeParamsProgress = undefined;
try { try {
await runWithBackendProgress( await runWithBackendProgress(
async () => { async () => {
const params = fsrsParams($config); const params = fsrsParams($config);
const resp = await computeFsrsWeights({ const resp = await computeFsrsParams({
search: $config.weightSearch search: $config.paramSearch
? $config.weightSearch ? $config.paramSearch
: defaultWeightSearch, : defaultparamSearch,
ignoreRevlogsBeforeMs: getIgnoreRevlogsBeforeMs(), ignoreRevlogsBeforeMs: getIgnoreRevlogsBeforeMs(),
currentWeights: params, currentParams: params,
}); });
if ( if (
(params.length && (params.length &&
params.every( 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); setTimeout(() => alert(tr.deckConfigFsrsParamsOptimal()), 100);
} else { } else {
$config.fsrsParams5 = resp.weights; $config.fsrsParams5 = resp.params;
} }
if (computeWeightsProgress) { if (computeParamsProgress) {
computeWeightsProgress.current = computeWeightsProgress.total; computeParamsProgress.current = computeParamsProgress.total;
} }
}, },
(progress) => { (progress) => {
if (progress.value.case === "computeWeights") { if (progress.value.case === "computeParams") {
computeWeightsProgress = progress.value.value; computeParamsProgress = progress.value.value;
} }
}, },
); );
} finally { } finally {
computingWeights = false; computingParams = false;
} }
} }
async function checkWeights(): Promise<void> { async function checkParams(): Promise<void> {
if (checkingWeights) { if (checkingParams) {
await setWantsAbort({}); await setWantsAbort({});
return; return;
} }
@ -180,21 +180,21 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
alert(tr.deckConfigPleaseSaveYourChangesFirst()); alert(tr.deckConfigPleaseSaveYourChangesFirst());
return; return;
} }
checkingWeights = true; checkingParams = true;
computeWeightsProgress = undefined; computeParamsProgress = undefined;
try { try {
await runWithBackendProgress( await runWithBackendProgress(
async () => { async () => {
const search = $config.weightSearch const search = $config.paramSearch
? $config.weightSearch ? $config.paramSearch
: defaultWeightSearch; : defaultparamSearch;
const resp = await evaluateWeights({ const resp = await evaluateParams({
weights: fsrsParams($config), params: fsrsParams($config),
search, search,
ignoreRevlogsBeforeMs: getIgnoreRevlogsBeforeMs(), ignoreRevlogsBeforeMs: getIgnoreRevlogsBeforeMs(),
}); });
if (computeWeightsProgress) { if (computeParamsProgress) {
computeWeightsProgress.current = computeWeightsProgress.total; computeParamsProgress.current = computeParamsProgress.total;
} }
setTimeout( setTimeout(
() => () =>
@ -207,13 +207,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
); );
}, },
(progress) => { (progress) => {
if (progress.value.case === "computeWeights") { if (progress.value.case === "computeParams") {
computeWeightsProgress = progress.value.value; computeParamsProgress = progress.value.value;
} }
}, },
); );
} finally { } 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( await runWithBackendProgress(
async () => { async () => {
optimalRetentionRequest.maxInterval = $config.maximumReviewInterval; optimalRetentionRequest.maxInterval = $config.maximumReviewInterval;
optimalRetentionRequest.weights = fsrsParams($config); optimalRetentionRequest.params = fsrsParams($config);
optimalRetentionRequest.search = `preset:"${state.getCurrentName()}" -is:suspended`; optimalRetentionRequest.search = `preset:"${state.getCurrentName()}" -is:suspended`;
const resp = await computeOptimalRetention(optimalRetentionRequest); const resp = await computeOptimalRetention(optimalRetentionRequest);
optimalRetention = resp.optimalRetention; 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( $: computeRetentionProgressString = renderRetentionProgress(
computeRetentionProgress, 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) { if (!val || !val.total) {
return ""; return "";
} }
@ -313,7 +313,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
try { try {
await runWithBackendProgress( await runWithBackendProgress(
async () => { async () => {
simulateFsrsRequest.weights = fsrsParams($config); simulateFsrsRequest.params = fsrsParams($config);
simulateFsrsRequest.desiredRetention = $config.desiredRetention; simulateFsrsRequest.desiredRetention = $config.desiredRetention;
simulateFsrsRequest.search = `preset:"${state.getCurrentName()}" -is:suspended`; simulateFsrsRequest.search = `preset:"${state.getCurrentName()}" -is:suspended`;
simulateProgressString = "processing..."; 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} /> <Warning warning={desiredRetentionWarning} className={retentionWarningClass} />
<div class="ms-1 me-1"> <div class="ms-1 me-1">
<WeightsInputRow <ParamsInputRow
bind:value={$config.fsrsParams5} bind:value={$config.fsrsParams5}
defaultValue={[]} defaultValue={[]}
defaults={defaults.fsrsParams5} defaults={defaults.fsrsParams5}
> >
<SettingTitle on:click={() => openHelpModal("modelWeights")}> <SettingTitle on:click={() => openHelpModal("modelParams")}>
{tr.deckConfigWeights()} {tr.deckConfigWeights()}
</SettingTitle> </SettingTitle>
</WeightsInputRow> </ParamsInputRow>
<WeightsSearchRow <ParamsSearchRow
bind:value={$config.weightSearch} bind:value={$config.paramSearch}
placeholder={defaultWeightSearch} placeholder={defaultparamSearch}
/> />
<button <button
class="btn {computingWeights ? 'btn-warning' : 'btn-primary'}" class="btn {computingParams ? 'btn-warning' : 'btn-primary'}"
disabled={!computingWeights && computing} disabled={!computingParams && computing}
on:click={() => computeWeights()} on:click={() => computeParams()}
> >
{#if computingWeights} {#if computingParams}
{tr.actionsCancel()} {tr.actionsCancel()}
{:else} {:else}
{tr.deckConfigOptimizeButton()} {tr.deckConfigOptimizeButton()}
{/if} {/if}
</button> </button>
<button <button
class="btn {checkingWeights ? 'btn-warning' : 'btn-primary'}" class="btn {checkingParams ? 'btn-warning' : 'btn-primary'}"
disabled={!checkingWeights && computing} disabled={!checkingParams && computing}
on:click={() => checkWeights()} on:click={() => checkParams()}
> >
{#if checkingWeights} {#if checkingParams}
{tr.actionsCancel()} {tr.actionsCancel()}
{:else} {:else}
{tr.deckConfigEvaluateButton()} {tr.deckConfigEvaluateButton()}
{/if} {/if}
</button> </button>
<div> <div>
{#if computingWeights || checkingWeights} {#if computingParams || checkingParams}
{computeWeightsProgressString} {computeParamsProgressString}
{:else if totalReviews !== undefined} {:else if totalReviews !== undefined}
{tr.statisticsReviews({ reviews: totalReviews })} {tr.statisticsReviews({ reviews: totalReviews })}
{/if} {/if}

View file

@ -37,7 +37,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
help: tr.deckConfigDesiredRetentionTooltip(), help: tr.deckConfigDesiredRetentionTooltip(),
sched: HelpItemScheduler.FSRS, sched: HelpItemScheduler.FSRS,
}, },
modelWeights: { modelParams: {
title: tr.deckConfigWeights(), title: tr.deckConfigWeights(),
help: help:
tr.deckConfigWeightsTooltip2() + 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; let stringValue: string;
$: stringValue = render(value); $: stringValue = render(value);
function render(weights: number[]): string { function render(params: number[]): string {
return weights.map((v) => v.toFixed(4)).join(", "); return params.map((v) => v.toFixed(4)).join(", ");
} }
function update(this: HTMLInputElement): void { function update(this: HTMLInputElement): void {

View file

@ -6,7 +6,7 @@
import ConfigInput from "$lib/components/ConfigInput.svelte"; import ConfigInput from "$lib/components/ConfigInput.svelte";
import RevertButton from "$lib/components/RevertButton.svelte"; import RevertButton from "$lib/components/RevertButton.svelte";
import WeightsInput from "./WeightsInput.svelte"; import ParamsInput from "./ParamsInput.svelte";
export let value: number[]; export let value: number[];
export let defaultValue: number[]; export let defaultValue: number[];
@ -15,6 +15,6 @@
<slot /> <slot />
<ConfigInput> <ConfigInput>
<WeightsInput bind:value {defaults} /> <ParamsInput bind:value {defaults} />
<RevertButton slot="revert" bind:value {defaultValue} /> <RevertButton slot="revert" bind:value {defaultValue} />
</ConfigInput> </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> { async function save(mode: UpdateDeckConfigsMode): Promise<void> {
await commitEditing(); await commitEditing();
if (mode === UpdateDeckConfigsMode.COMPUTE_ALL_WEIGHTS && !get(state.fsrs)) { if (mode === UpdateDeckConfigsMode.COMPUTE_ALL_PARAMS && !get(state.fsrs)) {
alert(tr.deckConfigFsrsMustBeEnabled()); alert(tr.deckConfigFsrsMustBeEnabled());
return; return;
} }
@ -119,7 +119,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{tr.deckConfigSaveToAllSubdecks()} {tr.deckConfigSaveToAllSubdecks()}
</DropdownItem> </DropdownItem>
<DropdownItem <DropdownItem
on:click={() => save(UpdateDeckConfigsMode.COMPUTE_ALL_WEIGHTS)} on:click={() => save(UpdateDeckConfigsMode.COMPUTE_ALL_PARAMS)}
> >
{tr.deckConfigSaveAndOptimize()} {tr.deckConfigSaveAndOptimize()}
</DropdownItem> </DropdownItem>