mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
Rename remaining 'weights' references to 'params'
This commit is contained in:
parent
c45fa518d2
commit
6adbd922f7
27 changed files with 194 additions and 200 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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",
|
||||||
]
|
]
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()))?;
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -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(¶ms))?;
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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| {
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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<()> {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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() +
|
||||||
|
|
|
@ -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 {
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue