mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
Feat/FSRS-6 (#3929)
* Feat/FSRS-6 * update comment * add decay to Card * ./ninja fix:minilints * pass check * fix NaN in evaluation * remove console * decay should fallback to 0.5 when it's None. * Update SimulatorModal.svelte * Update a few comments * Update FSRS decay defaults to use constants for better maintainability and clarity * Update rslib/src/storage/card/data.rs
This commit is contained in:
parent
1e6c8b2006
commit
e096c462fa
31 changed files with 312 additions and 181 deletions
154
Cargo.lock
generated
154
Cargo.lock
generated
|
@ -142,7 +142,7 @@ dependencies = [
|
|||
"serde_tuple",
|
||||
"sha1",
|
||||
"snafu",
|
||||
"strum",
|
||||
"strum 0.26.3",
|
||||
"syn 2.0.96",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
|
@ -218,7 +218,7 @@ dependencies = [
|
|||
"prost-types",
|
||||
"serde",
|
||||
"snafu",
|
||||
"strum",
|
||||
"strum 0.26.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -574,11 +574,12 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
|||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "2.0.0-rc.3"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95"
|
||||
checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"unty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -664,9 +665,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
|||
|
||||
[[package]]
|
||||
name = "burn"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55af4c56b540bcf00cf1c7e13b1c60644734906495048afbd4a79aabd0a6efbe"
|
||||
checksum = "22149f3b5ab6628e9e9c0b29156b906d32d36bbf76f2c34ad5ce1801f5b4486e"
|
||||
dependencies = [
|
||||
"burn-core",
|
||||
"burn-train",
|
||||
|
@ -674,9 +675,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "burn-autodiff"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa53181463ef16220438e240f10e1e8cb2fcf1824dbc33b8f259a454ff5f46f"
|
||||
checksum = "f2167ab07f9be5f2a027accba92d8dde02ea905f35844f8529bb2533b4fc8646"
|
||||
dependencies = [
|
||||
"burn-common",
|
||||
"burn-tensor",
|
||||
|
@ -687,9 +688,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "burn-candle"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b49a6da72c10ac552b3c023d74dade9714c10aac0fc5f33cfc4ca389463b99e"
|
||||
checksum = "aeef1204c4d33dd71a9628a311178eb149131c65234eb64e8201e27cf1ee1ba0"
|
||||
dependencies = [
|
||||
"burn-tensor",
|
||||
"candle-core",
|
||||
|
@ -699,9 +700,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "burn-common"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a1471949b06002c984df9d753a084a79149841dd7935911d9e432b8478f9fd5"
|
||||
checksum = "fb516d1faa50628828b3c2b79db3e483f20d62966f7dae68c6f21743f5f7e8ef"
|
||||
dependencies = [
|
||||
"cubecl-common",
|
||||
"getrandom 0.2.15",
|
||||
|
@ -712,9 +713,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "burn-core"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f8ebbf7d5c8bdc269260bd8e7ce08e488e6625da19b3d80ca34a729d78a77ab"
|
||||
checksum = "594c44ac9f2996c2c0b92f5a44a1287d41fca3954182601a4a29b628a5973357"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"bincode",
|
||||
|
@ -747,9 +748,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "burn-cuda"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90534d6c7f909a8cad49470921dc3eb2b118f7a3c8bde313defba3f4cae3ac3"
|
||||
checksum = "08fe1e5f285214d16cfd298453b807675c9a4ed742a35c8807be42af69e8ee97"
|
||||
dependencies = [
|
||||
"burn-jit",
|
||||
"burn-tensor",
|
||||
|
@ -762,9 +763,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "burn-dataset"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b851cb5165da57871bed2c48a29673dde0ddbd198a39b2a37411b6adf6df6ad"
|
||||
checksum = "92a5cde6c09c751fb6aafca10d8e18faa42fe18eef44b4768851575c20db6904"
|
||||
dependencies = [
|
||||
"csv",
|
||||
"derive-new 0.7.0",
|
||||
|
@ -774,17 +775,17 @@ dependencies = [
|
|||
"sanitize-filename 0.6.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"strum 0.26.3",
|
||||
"strum_macros 0.26.4",
|
||||
"tempfile",
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "burn-derive"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f784ffe0df57848ba232e5f40a1c1f5df3571df59bec99ba32bc7610fd9e811"
|
||||
checksum = "2bb8f828a681946b07a87750ed0593d885e7b101653bd6a3bb1942976156bb48"
|
||||
dependencies = [
|
||||
"derive-new 0.7.0",
|
||||
"proc-macro2",
|
||||
|
@ -794,9 +795,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "burn-hip"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd9fbfee77b3d2b67bf434b883ec6ed73f4f9bf1fd8d59f9dde217c7a4b5285d"
|
||||
checksum = "84191ed69af8c48a133c05e0ee4dfe73d7a3b8f96e3ceec899b4f85b19072232"
|
||||
dependencies = [
|
||||
"burn-jit",
|
||||
"burn-tensor",
|
||||
|
@ -809,9 +810,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "burn-jit"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb6b06689c4e8d6cfdcaf0b0e168e58a931c3935414e48f4e3e3e85e8d7a77a0"
|
||||
checksum = "6b723ddb46032953c4fb908feca57b470b0c9839f808fcd52de04a8510b88a23"
|
||||
dependencies = [
|
||||
"burn-common",
|
||||
"burn-tensor",
|
||||
|
@ -831,9 +832,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "burn-ndarray"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "419fa3eda8cf9fddce0d156946b3d46642c10a41569b23e7855f775f862d310a"
|
||||
checksum = "1b8ce3bd0f1e792b53610d291eb463d9790449688c4455a496c46266e46a179f"
|
||||
dependencies = [
|
||||
"atomic_float",
|
||||
"burn-autodiff",
|
||||
|
@ -842,7 +843,7 @@ dependencies = [
|
|||
"derive-new 0.7.0",
|
||||
"libm",
|
||||
"matrixmultiply",
|
||||
"ndarray 0.16.1",
|
||||
"ndarray",
|
||||
"num-traits",
|
||||
"portable-atomic-util",
|
||||
"rand",
|
||||
|
@ -851,9 +852,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "burn-router"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c39bdb6d5c749221741a362da9b3ea3157304f831ab4b4a6902725a1efaea159"
|
||||
checksum = "b6a1a6cb08eccb65b112bc5853ac35c88faa8b04b03270bcfb1ac8a253a066bb"
|
||||
dependencies = [
|
||||
"burn-common",
|
||||
"burn-tensor",
|
||||
|
@ -864,9 +865,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "burn-tensor"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24db20273a636d5340e5a29af142722e0a657491e6b3cfcceb1e62eb862b3b37"
|
||||
checksum = "ab959e7da2e7514b959d841c93e8e026233aa77284f5d976099a8f5251e3ba99"
|
||||
dependencies = [
|
||||
"burn-common",
|
||||
"bytemuck",
|
||||
|
@ -885,9 +886,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "burn-train"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "714298cbc0c41f48d53cb1e6aeb6203b49b6110620517f69fbcc37a9b41cb6c8"
|
||||
checksum = "c004e8c761ad50c568739581a2dab38aa2f4db723183fb189f130e6d2b4e0d1c"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"burn-core",
|
||||
|
@ -906,9 +907,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "burn-wgpu"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ef5b6c56da563a708b2da16f0559a061e7b93f3acae63903734ee978c9b9f93"
|
||||
checksum = "5f80a3413527087e73042c807d41d6fd3a1e8a28eb519e3ad5e91f6354cbfb9d"
|
||||
dependencies = [
|
||||
"burn-jit",
|
||||
"burn-tensor",
|
||||
|
@ -2099,19 +2100,19 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "fsrs"
|
||||
version = "3.0.0"
|
||||
source = "git+https://github.com/open-spaced-repetition/fsrs-rs.git?rev=08d90d1363b0c4722422bf0ef71ed8fd7d053f8a#08d90d1363b0c4722422bf0ef71ed8fd7d053f8a"
|
||||
source = "git+https://github.com/open-spaced-repetition/fsrs-rs.git?rev=c7717682997a8a6d53d97c7196281e745c5b3c8e#c7717682997a8a6d53d97c7196281e745c5b3c8e"
|
||||
dependencies = [
|
||||
"burn",
|
||||
"itertools 0.12.1",
|
||||
"itertools 0.14.0",
|
||||
"log",
|
||||
"ndarray 0.15.6",
|
||||
"ndarray",
|
||||
"ndarray-rand",
|
||||
"priority-queue",
|
||||
"rand",
|
||||
"rayon",
|
||||
"serde",
|
||||
"snafu",
|
||||
"strum",
|
||||
"strum 0.27.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3254,18 +3255,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
@ -3441,7 +3442,7 @@ dependencies = [
|
|||
"linkcheck",
|
||||
"regex",
|
||||
"reqwest 0.12.8",
|
||||
"strum",
|
||||
"strum 0.26.3",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
@ -3831,19 +3832,6 @@ dependencies = [
|
|||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndarray"
|
||||
version = "0.15.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
|
||||
dependencies = [
|
||||
"matrixmultiply",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"rawpointer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndarray"
|
||||
version = "0.16.1"
|
||||
|
@ -3862,11 +3850,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ndarray-rand"
|
||||
version = "0.14.0"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65608f937acc725f5b164dcf40f4f0bc5d67dc268ab8a649d3002606718c4588"
|
||||
checksum = "f093b3db6fd194718dcdeea6bd8c829417deae904e3fcc7732dabcd4416d25d8"
|
||||
dependencies = [
|
||||
"ndarray 0.15.6",
|
||||
"ndarray",
|
||||
"rand",
|
||||
"rand_distr",
|
||||
]
|
||||
|
@ -4608,9 +4596,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "priority-queue"
|
||||
version = "2.1.1"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "714c75db297bc88a63783ffc6ab9f830698a6705aa0201416931759ef4c8183d"
|
||||
checksum = "ef08705fa1589a1a59aa924ad77d14722cb0cd97b67dd5004ed5f4a4873fce8d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"equivalent",
|
||||
|
@ -5561,9 +5549,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
@ -5590,9 +5578,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -5869,7 +5857,16 @@ version = "0.26.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
"strum_macros 0.26.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
|
||||
dependencies = [
|
||||
"strum_macros 0.27.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5885,6 +5882,19 @@ dependencies = [
|
|||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
|
@ -6702,6 +6712,12 @@ version = "0.9.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "unty"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.4"
|
||||
|
|
|
@ -37,7 +37,7 @@ rev = "184b2ca50ed39ca43da13f0b830a463861adb9ca"
|
|||
[workspace.dependencies.fsrs]
|
||||
# version = "=2.0.3"
|
||||
git = "https://github.com/open-spaced-repetition/fsrs-rs.git"
|
||||
rev = "08d90d1363b0c4722422bf0ef71ed8fd7d053f8a"
|
||||
rev = "c7717682997a8a6d53d97c7196281e745c5b3c8e"
|
||||
# path = "../open-spaced-repetition/fsrs-rs"
|
||||
|
||||
[workspace.dependencies]
|
||||
|
|
|
@ -334,7 +334,7 @@
|
|||
},
|
||||
{
|
||||
"name": "bincode",
|
||||
"version": "2.0.0-rc.3",
|
||||
"version": "2.0.1",
|
||||
"authors": "Ty Overby <ty@pre-alpha.com>|Zoey Riordan <zoey@dos.cafe>|Victor Koenders <bincode@trangar.com>",
|
||||
"repository": "https://github.com/bincode-org/bincode",
|
||||
"license": "MIT",
|
||||
|
@ -415,7 +415,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "nathanielsimard <nathaniel.simard.42@gmail.com>",
|
||||
"repository": "https://github.com/tracel-ai/burn",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -424,7 +424,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn-autodiff",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "nathanielsimard <nathaniel.simard.42@gmail.com>",
|
||||
"repository": "https://github.com/tracel-ai/burn/tree/main/crates/burn-autodiff",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -433,7 +433,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn-candle",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "louisfd <louisfd94@gmail.com>",
|
||||
"repository": "https://github.com/tracel-ai/burn/tree/main/crates/burn-candle",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -442,7 +442,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn-common",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "Dilshod Tadjibaev (@antimora)",
|
||||
"repository": "https://github.com/tracel-ai/burn/tree/main/crates/burn-common",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -451,7 +451,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn-core",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "nathanielsimard <nathaniel.simard.42@gmail.com>",
|
||||
"repository": "https://github.com/tracel-ai/burn/tree/main/crates/burn-core",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -460,7 +460,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn-cuda",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "nathanielsimard <nathaniel.simard.42@gmail.com>",
|
||||
"repository": "https://github.com/tracel-ai/burn/tree/main/crates/burn-cuda",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -469,7 +469,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn-dataset",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "nathanielsimard <nathaniel.simard.42@gmail.com>",
|
||||
"repository": "https://github.com/tracel-ai/burn/tree/main/crates/burn-dataset",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -478,7 +478,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn-derive",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "nathanielsimard <nathaniel.simard.42@gmail.com>",
|
||||
"repository": "https://github.com/tracel-ai/burn/tree/main/crates/burn-derive",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -487,7 +487,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn-hip",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "nathanielsimard <nathaniel.simard.42@gmail.com>",
|
||||
"repository": "https://github.com/tracel-ai/burn/tree/main/crates/burn-hip",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -496,7 +496,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn-jit",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "nathanielsimard <nathaniel.simard.42@gmail.com>",
|
||||
"repository": "https://github.com/tracel-ai/burn/tree/main/crates/burn-jit",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -505,7 +505,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn-ndarray",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "nathanielsimard <nathaniel.simard.42@gmail.com>",
|
||||
"repository": "https://github.com/tracel-ai/burn/tree/main/crates/burn-ndarray",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -514,7 +514,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn-router",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "guillaumelagrange <lagrange.guillaume.1@gmail.com>|nathanielsimard <nathaniel.simard.42@gmail.com>",
|
||||
"repository": "https://github.com/tracel-ai/burn/tree/main/crates/burn-router",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -523,7 +523,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn-tensor",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "nathanielsimard <nathaniel.simard.42@gmail.com>",
|
||||
"repository": "https://github.com/tracel-ai/burn/tree/main/crates/burn-tensor",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -532,7 +532,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn-train",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "nathanielsimard <nathaniel.simard.42@gmail.com>",
|
||||
"repository": "https://github.com/tracel-ai/burn/tree/main/crates/burn-train",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -541,7 +541,7 @@
|
|||
},
|
||||
{
|
||||
"name": "burn-wgpu",
|
||||
"version": "0.16.0",
|
||||
"version": "0.16.1",
|
||||
"authors": "nathanielsimard <nathaniel.simard.42@gmail.com>",
|
||||
"repository": "https://github.com/tracel-ai/burn/tree/main/crates/burn-wgpu",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -2071,7 +2071,7 @@
|
|||
},
|
||||
{
|
||||
"name": "itertools",
|
||||
"version": "0.12.1",
|
||||
"version": "0.13.0",
|
||||
"authors": "bluss",
|
||||
"repository": "https://github.com/rust-itertools/itertools",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -2080,7 +2080,7 @@
|
|||
},
|
||||
{
|
||||
"name": "itertools",
|
||||
"version": "0.13.0",
|
||||
"version": "0.14.0",
|
||||
"authors": "bluss",
|
||||
"repository": "https://github.com/rust-itertools/itertools",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -2429,15 +2429,6 @@
|
|||
"license_file": null,
|
||||
"description": "A wrapper over a platform's native TLS implementation"
|
||||
},
|
||||
{
|
||||
"name": "ndarray",
|
||||
"version": "0.15.6",
|
||||
"authors": "Ulrik Sverdrup \"bluss\"|Jim Turner",
|
||||
"repository": "https://github.com/rust-ndarray/ndarray",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"license_file": null,
|
||||
"description": "An n-dimensional array for general elements and for numerics. Lightweight array views and slicing; views support chunking and splitting."
|
||||
},
|
||||
{
|
||||
"name": "ndarray",
|
||||
"version": "0.16.1",
|
||||
|
@ -2449,7 +2440,7 @@
|
|||
},
|
||||
{
|
||||
"name": "ndarray-rand",
|
||||
"version": "0.14.0",
|
||||
"version": "0.15.0",
|
||||
"authors": "bluss",
|
||||
"repository": "https://github.com/rust-ndarray/ndarray",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -2971,7 +2962,7 @@
|
|||
},
|
||||
{
|
||||
"name": "priority-queue",
|
||||
"version": "2.1.1",
|
||||
"version": "2.3.1",
|
||||
"authors": "Gianmarco Garrisi <gianmarcogarrisi@tutanota.com>",
|
||||
"repository": "https://github.com/garro95/priority-queue",
|
||||
"license": "LGPL-3.0-or-later OR MPL-2.0",
|
||||
|
@ -3574,7 +3565,7 @@
|
|||
},
|
||||
{
|
||||
"name": "serde",
|
||||
"version": "1.0.217",
|
||||
"version": "1.0.219",
|
||||
"authors": "Erick Tryzelaar <erick.tryzelaar@gmail.com>|David Tolnay <dtolnay@gmail.com>",
|
||||
"repository": "https://github.com/serde-rs/serde",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -3601,7 +3592,7 @@
|
|||
},
|
||||
{
|
||||
"name": "serde_derive",
|
||||
"version": "1.0.217",
|
||||
"version": "1.0.219",
|
||||
"authors": "Erick Tryzelaar <erick.tryzelaar@gmail.com>|David Tolnay <dtolnay@gmail.com>",
|
||||
"repository": "https://github.com/serde-rs/serde",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
|
@ -3851,6 +3842,15 @@
|
|||
"license_file": null,
|
||||
"description": "Helpful macros for working with enums and strings"
|
||||
},
|
||||
{
|
||||
"name": "strum",
|
||||
"version": "0.27.1",
|
||||
"authors": "Peter Glotfelty <peter.glotfelty@microsoft.com>",
|
||||
"repository": "https://github.com/Peternator7/strum",
|
||||
"license": "MIT",
|
||||
"license_file": null,
|
||||
"description": "Helpful macros for working with enums and strings"
|
||||
},
|
||||
{
|
||||
"name": "strum_macros",
|
||||
"version": "0.26.4",
|
||||
|
@ -3860,6 +3860,15 @@
|
|||
"license_file": null,
|
||||
"description": "Helpful macros for working with enums and strings"
|
||||
},
|
||||
{
|
||||
"name": "strum_macros",
|
||||
"version": "0.27.1",
|
||||
"authors": "Peter Glotfelty <peter.glotfelty@microsoft.com>",
|
||||
"repository": "https://github.com/Peternator7/strum",
|
||||
"license": "MIT",
|
||||
"license_file": null,
|
||||
"description": "Helpful macros for working with enums and strings"
|
||||
},
|
||||
{
|
||||
"name": "subtle",
|
||||
"version": "2.6.1",
|
||||
|
@ -4427,6 +4436,15 @@
|
|||
"license_file": null,
|
||||
"description": "Safe, fast, zero-panic, zero-crashing, zero-allocation parsing of untrusted inputs in Rust."
|
||||
},
|
||||
{
|
||||
"name": "unty",
|
||||
"version": "0.0.4",
|
||||
"authors": "Victor Koenders <bincode@trang.ar>",
|
||||
"repository": "https://github.com/bincode-org/unty",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"license_file": null,
|
||||
"description": "Explicitly types your generics"
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"version": "2.5.4",
|
||||
|
|
|
@ -50,6 +50,7 @@ message Card {
|
|||
optional uint32 original_position = 18;
|
||||
optional FsrsMemoryState memory_state = 20;
|
||||
optional float desired_retention = 21;
|
||||
optional float decay = 22;
|
||||
string custom_data = 19;
|
||||
}
|
||||
|
||||
|
|
|
@ -122,9 +122,10 @@ message DeckConfig {
|
|||
|
||||
repeated float fsrs_params_4 = 3;
|
||||
repeated float fsrs_params_5 = 5;
|
||||
repeated float fsrs_params_6 = 6;
|
||||
|
||||
// consider saving remaining ones for fsrs param changes
|
||||
reserved 6 to 8;
|
||||
reserved 7 to 8;
|
||||
|
||||
uint32 new_per_day = 9;
|
||||
uint32 reviews_per_day = 10;
|
||||
|
|
|
@ -432,17 +432,16 @@ message GetOptimalRetentionParametersResponse {
|
|||
uint32 learn_span = 2;
|
||||
float max_cost_perday = 3;
|
||||
float max_ivl = 4;
|
||||
repeated float learn_costs = 5;
|
||||
repeated float review_costs = 6;
|
||||
repeated float first_rating_prob = 7;
|
||||
repeated float review_rating_prob = 8;
|
||||
repeated float first_rating_offsets = 9;
|
||||
repeated float first_session_lens = 10;
|
||||
float forget_rating_offset = 11;
|
||||
float forget_session_len = 12;
|
||||
float loss_aversion = 13;
|
||||
uint32 learn_limit = 14;
|
||||
uint32 review_limit = 15;
|
||||
repeated float first_rating_prob = 5;
|
||||
repeated float review_rating_prob = 6;
|
||||
float loss_aversion = 7;
|
||||
uint32 learn_limit = 8;
|
||||
uint32 review_limit = 9;
|
||||
repeated float learning_step_transitions = 10;
|
||||
repeated float relearning_step_transitions = 11;
|
||||
repeated float state_rating_costs = 12;
|
||||
uint32 learning_step_count = 13;
|
||||
uint32 relearning_step_count = 14;
|
||||
}
|
||||
|
||||
message EvaluateParamsRequest {
|
||||
|
|
|
@ -65,6 +65,7 @@ message CardStatsResponse {
|
|||
string preset = 21;
|
||||
optional string original_deck = 22;
|
||||
optional float desired_retention = 23;
|
||||
repeated float fsrs_params = 24;
|
||||
}
|
||||
|
||||
message GraphsRequest {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use fsrs::FSRS;
|
||||
use fsrs::FSRS5_DEFAULT_DECAY;
|
||||
use itertools::Itertools;
|
||||
use strum::Display;
|
||||
use strum::EnumIter;
|
||||
|
@ -541,10 +542,13 @@ impl RowContext {
|
|||
.memory_state
|
||||
.as_ref()
|
||||
.zip(self.cards[0].days_since_last_review(&self.timing))
|
||||
.map(|(state, days_elapsed)| {
|
||||
let r = FSRS::new(None)
|
||||
.unwrap()
|
||||
.current_retrievability((*state).into(), days_elapsed);
|
||||
.zip(Some(self.cards[0].decay.unwrap_or(FSRS5_DEFAULT_DECAY)))
|
||||
.map(|((state, days_elapsed), decay)| {
|
||||
let r = FSRS::new(None).unwrap().current_retrievability(
|
||||
(*state).into(),
|
||||
days_elapsed,
|
||||
decay,
|
||||
);
|
||||
format!("{:.0}%", r * 100.)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
|
|
|
@ -95,6 +95,7 @@ pub struct Card {
|
|||
pub(crate) original_position: Option<u32>,
|
||||
pub(crate) memory_state: Option<FsrsMemoryState>,
|
||||
pub(crate) desired_retention: Option<f32>,
|
||||
pub(crate) decay: Option<f32>,
|
||||
/// JSON object or empty; exposed through the reviewer for persisting custom
|
||||
/// state
|
||||
pub(crate) custom_data: String,
|
||||
|
@ -145,6 +146,7 @@ impl Default for Card {
|
|||
original_position: None,
|
||||
memory_state: None,
|
||||
desired_retention: None,
|
||||
decay: None,
|
||||
custom_data: String::new(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,6 +106,7 @@ impl TryFrom<anki_proto::cards::Card> for Card {
|
|||
original_position: c.original_position,
|
||||
memory_state: c.memory_state.map(Into::into),
|
||||
desired_retention: c.desired_retention,
|
||||
decay: c.decay,
|
||||
custom_data: c.custom_data,
|
||||
})
|
||||
}
|
||||
|
@ -134,6 +135,7 @@ impl From<Card> for anki_proto::cards::Card {
|
|||
original_position: c.original_position,
|
||||
memory_state: c.memory_state.map(Into::into),
|
||||
desired_retention: c.desired_retention,
|
||||
decay: c.decay,
|
||||
custom_data: c.custom_data,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ const DEFAULT_DECK_CONFIG_INNER: DeckConfigInner = DeckConfigInner {
|
|||
bury_interday_learning: false,
|
||||
fsrs_params_4: vec![],
|
||||
fsrs_params_5: vec![],
|
||||
fsrs_params_6: vec![],
|
||||
desired_retention: 0.9,
|
||||
other: Vec::new(),
|
||||
historical_retention: 0.9,
|
||||
|
@ -107,9 +108,11 @@ impl DeckConfig {
|
|||
self.usn = usn;
|
||||
}
|
||||
|
||||
/// Retrieve the FSRS 5.0 params, falling back on 4.x ones.
|
||||
/// Retrieve the FSRS 6.0 params, falling back on 5.0 or 4.x ones.
|
||||
pub fn fsrs_params(&self) -> &Vec<f32> {
|
||||
if self.inner.fsrs_params_5.len() == 19 {
|
||||
if self.inner.fsrs_params_6.len() == 21 {
|
||||
&self.inner.fsrs_params_6
|
||||
} else if self.inner.fsrs_params_5.len() == 19 {
|
||||
&self.inner.fsrs_params_5
|
||||
} else {
|
||||
&self.inner.fsrs_params_4
|
||||
|
|
|
@ -74,6 +74,8 @@ pub struct DeckConfSchema11 {
|
|||
#[serde(default)]
|
||||
fsrs_params_5: Vec<f32>,
|
||||
#[serde(default)]
|
||||
fsrs_params_6: Vec<f32>,
|
||||
#[serde(default)]
|
||||
desired_retention: f32,
|
||||
#[serde(default)]
|
||||
ignore_revlogs_before_date: String,
|
||||
|
@ -310,6 +312,7 @@ impl Default for DeckConfSchema11 {
|
|||
bury_interday_learning: false,
|
||||
fsrs_params_4: vec![],
|
||||
fsrs_params_5: vec![],
|
||||
fsrs_params_6: vec![],
|
||||
desired_retention: 0.9,
|
||||
sm2_retention: 0.9,
|
||||
param_search: "".to_string(),
|
||||
|
@ -391,6 +394,7 @@ impl From<DeckConfSchema11> for DeckConfig {
|
|||
bury_interday_learning: c.bury_interday_learning,
|
||||
fsrs_params_4: c.fsrs_params_4,
|
||||
fsrs_params_5: c.fsrs_params_5,
|
||||
fsrs_params_6: c.fsrs_params_6,
|
||||
ignore_revlogs_before_date: c.ignore_revlogs_before_date,
|
||||
easy_days_percentages: c.easy_days_percentages,
|
||||
desired_retention: c.desired_retention,
|
||||
|
@ -504,6 +508,7 @@ impl From<DeckConfig> for DeckConfSchema11 {
|
|||
bury_interday_learning: i.bury_interday_learning,
|
||||
fsrs_params_4: i.fsrs_params_4,
|
||||
fsrs_params_5: i.fsrs_params_5,
|
||||
fsrs_params_6: i.fsrs_params_6,
|
||||
desired_retention: i.desired_retention,
|
||||
sm2_retention: i.historical_retention,
|
||||
param_search: i.param_search,
|
||||
|
@ -532,6 +537,7 @@ static RESERVED_DECKCONF_KEYS: Set<&'static str> = phf_set! {
|
|||
"newGatherPriority",
|
||||
"fsrsWeights",
|
||||
"fsrsParams5",
|
||||
"fsrsParams6",
|
||||
"desiredRetention",
|
||||
"stopTimerOnAnswer",
|
||||
"secondsToShowQuestion",
|
||||
|
|
|
@ -50,7 +50,7 @@ impl Collection {
|
|||
deck: DeckId,
|
||||
) -> Result<anki_proto::deck_config::DeckConfigsForUpdate> {
|
||||
let mut defaults = DeckConfig::default();
|
||||
defaults.inner.fsrs_params_5 = DEFAULT_PARAMETERS.into();
|
||||
defaults.inner.fsrs_params_6 = DEFAULT_PARAMETERS.into();
|
||||
let last_optimize = self.get_config_i32(I32ConfigKey::LastFsrsOptimize) as u32;
|
||||
let days_since_last_fsrs_optimize = if last_optimize > 0 {
|
||||
self.timing_today()?
|
||||
|
@ -88,10 +88,14 @@ impl Collection {
|
|||
// grab the config and sort it
|
||||
let mut config = self.storage.all_deck_config()?;
|
||||
config.sort_unstable_by(|a, b| a.name.cmp(&b.name));
|
||||
// pre-fill empty fsrs 5 params with 4 params
|
||||
// pre-fill empty fsrs params with older params
|
||||
config.iter_mut().for_each(|c| {
|
||||
if c.inner.fsrs_params_5.is_empty() {
|
||||
c.inner.fsrs_params_5 = c.inner.fsrs_params_4.clone();
|
||||
if c.inner.fsrs_params_6.is_empty() {
|
||||
c.inner.fsrs_params_6 = if c.inner.fsrs_params_5.is_empty() {
|
||||
c.inner.fsrs_params_4.clone()
|
||||
} else {
|
||||
c.inner.fsrs_params_5.clone()
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -165,10 +169,11 @@ impl Collection {
|
|||
|
||||
// add/update provided configs
|
||||
for conf in &mut req.configs {
|
||||
// If the user has provided empty FSRS5 params, zero out any
|
||||
// If the user has provided empty FSRS6 params, zero out any
|
||||
// old params as well, so we don't fall back on them, which would
|
||||
// be surprising as they're not shown in the GUI.
|
||||
if conf.inner.fsrs_params_5.is_empty() {
|
||||
if conf.inner.fsrs_params_6.is_empty() {
|
||||
conf.inner.fsrs_params_5.clear();
|
||||
conf.inner.fsrs_params_4.clear();
|
||||
}
|
||||
// check the provided parameters are valid before we save them
|
||||
|
@ -370,7 +375,7 @@ impl Collection {
|
|||
) {
|
||||
Ok(params) => {
|
||||
println!("{}: {:?}", config.name, params.params);
|
||||
config.inner.fsrs_params_5 = params.params;
|
||||
config.inner.fsrs_params_6 = params.params;
|
||||
}
|
||||
Err(AnkiError::Interrupted) => return Err(AnkiError::Interrupted),
|
||||
Err(err) => {
|
||||
|
|
|
@ -7,6 +7,8 @@ use anki_proto::scheduler::ComputeMemoryStateResponse;
|
|||
use fsrs::FSRSItem;
|
||||
use fsrs::MemoryState;
|
||||
use fsrs::FSRS;
|
||||
use fsrs::FSRS5_DEFAULT_DECAY;
|
||||
use fsrs::FSRS6_DEFAULT_DECAY;
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::params::ignore_revlogs_before_ms_from_config;
|
||||
|
@ -76,6 +78,15 @@ impl Collection {
|
|||
.then(|| Rescheduler::new(self))
|
||||
.transpose()?;
|
||||
let fsrs = FSRS::new(req.as_ref().map(|w| &w.params[..]).or(Some([].as_slice())))?;
|
||||
let decay = req.as_ref().map(|w| {
|
||||
if w.params.is_empty() {
|
||||
FSRS6_DEFAULT_DECAY // default decay for FSRS-6
|
||||
} else if w.params.len() < 21 {
|
||||
FSRS5_DEFAULT_DECAY // default decay for FSRS-4.5 and FSRS-5
|
||||
} else {
|
||||
w.params[20]
|
||||
}
|
||||
});
|
||||
let historical_retention = req.as_ref().map(|w| w.historical_retention);
|
||||
let items = fsrs_items_for_memory_states(
|
||||
&fsrs,
|
||||
|
@ -94,6 +105,7 @@ impl Collection {
|
|||
if let (Some(req), Some(item)) = (&req, item) {
|
||||
card.set_memory_state(&fsrs, Some(item), historical_retention.unwrap())?;
|
||||
card.desired_retention = desired_retention;
|
||||
card.decay = decay;
|
||||
// if rescheduling
|
||||
if let Some(reviews) = &last_revlog_info {
|
||||
// and we have a last review time for the card
|
||||
|
|
|
@ -66,21 +66,19 @@ impl Collection {
|
|||
learn_span: req.days_to_simulate as usize,
|
||||
max_cost_perday: f32::MAX,
|
||||
max_ivl: req.max_interval as f32,
|
||||
learn_costs: p.learn_costs,
|
||||
review_costs: p.review_costs,
|
||||
first_rating_prob: p.first_rating_prob,
|
||||
review_rating_prob: p.review_rating_prob,
|
||||
first_rating_offsets: p.first_rating_offsets,
|
||||
first_session_lens: p.first_session_lens,
|
||||
forget_rating_offset: p.forget_rating_offset,
|
||||
forget_session_len: p.forget_session_len,
|
||||
loss_aversion: req.loss_aversion as f32,
|
||||
learn_limit,
|
||||
review_limit: usize::MAX,
|
||||
new_cards_ignore_review_limit: true,
|
||||
suspend_after_lapses: None,
|
||||
post_scheduling_fn,
|
||||
review_priority_fn: None,
|
||||
learning_step_transitions: p.learning_step_transitions,
|
||||
relearning_step_transitions: p.relearning_step_transitions,
|
||||
state_rating_costs: p.state_rating_costs,
|
||||
learning_step_count: p.learning_step_count,
|
||||
relearning_step_count: p.relearning_step_count,
|
||||
},
|
||||
&req.params,
|
||||
|ip| {
|
||||
|
|
|
@ -75,6 +75,7 @@ pub(crate) fn apply_load_balance_and_easy_days(
|
|||
fn create_review_priority_fn(
|
||||
review_order: ReviewCardOrder,
|
||||
deck_size: usize,
|
||||
params: Vec<f32>,
|
||||
) -> Option<ReviewPriorityFn> {
|
||||
// Helper macro to wrap closure in ReviewPriorityFn
|
||||
macro_rules! wrap {
|
||||
|
@ -91,11 +92,12 @@ fn create_review_priority_fn(
|
|||
// Interval-based ordering
|
||||
IntervalsAscending => wrap!(|c| c.interval as i32),
|
||||
IntervalsDescending => wrap!(|c| -(c.interval as i32)),
|
||||
|
||||
// Retrievability-based ordering
|
||||
RetrievabilityAscending => wrap!(|c| (c.retrievability() * 1000.0) as i32),
|
||||
RetrievabilityAscending => {
|
||||
wrap!(move |c| (c.retrievability(¶ms) * 1000.0) as i32)
|
||||
}
|
||||
RetrievabilityDescending => {
|
||||
wrap!(|c| -(c.retrievability() * 1000.0) as i32)
|
||||
wrap!(move |c| -(c.retrievability(¶ms) * 1000.0) as i32)
|
||||
}
|
||||
|
||||
// Due date ordering
|
||||
|
@ -195,28 +197,26 @@ impl Collection {
|
|||
.review_order
|
||||
.try_into()
|
||||
.ok()
|
||||
.and_then(|order| create_review_priority_fn(order, deck_size));
|
||||
.and_then(|order| create_review_priority_fn(order, deck_size, req.params.clone()));
|
||||
|
||||
let config = SimulatorConfig {
|
||||
deck_size,
|
||||
learn_span: req.days_to_simulate as usize,
|
||||
max_cost_perday: f32::MAX,
|
||||
max_ivl: req.max_interval as f32,
|
||||
learn_costs: p.learn_costs,
|
||||
review_costs: p.review_costs,
|
||||
first_rating_prob: p.first_rating_prob,
|
||||
review_rating_prob: p.review_rating_prob,
|
||||
first_rating_offsets: p.first_rating_offsets,
|
||||
first_session_lens: p.first_session_lens,
|
||||
forget_rating_offset: p.forget_rating_offset,
|
||||
forget_session_len: p.forget_session_len,
|
||||
loss_aversion: 1.0,
|
||||
learn_limit: req.new_limit as usize,
|
||||
review_limit: req.review_limit as usize,
|
||||
new_cards_ignore_review_limit: req.new_cards_ignore_review_limit,
|
||||
suspend_after_lapses: req.suspend_after_lapse_count,
|
||||
post_scheduling_fn,
|
||||
review_priority_fn,
|
||||
learning_step_transitions: p.learning_step_transitions,
|
||||
relearning_step_transitions: p.relearning_step_transitions,
|
||||
state_rating_costs: p.state_rating_costs,
|
||||
learning_step_count: p.learning_step_count,
|
||||
relearning_step_count: p.relearning_step_count,
|
||||
};
|
||||
let result = simulate(
|
||||
&config,
|
||||
|
|
|
@ -321,17 +321,31 @@ impl crate::services::SchedulerService for Collection {
|
|||
learn_span: simulator_config.learn_span as u32,
|
||||
max_cost_perday: simulator_config.max_cost_perday,
|
||||
max_ivl: simulator_config.max_ivl,
|
||||
learn_costs: simulator_config.learn_costs.to_vec(),
|
||||
review_costs: simulator_config.review_costs.to_vec(),
|
||||
first_rating_prob: simulator_config.first_rating_prob.to_vec(),
|
||||
review_rating_prob: simulator_config.review_rating_prob.to_vec(),
|
||||
first_rating_offsets: simulator_config.first_rating_offsets.to_vec(),
|
||||
first_session_lens: simulator_config.first_session_lens.to_vec(),
|
||||
forget_rating_offset: simulator_config.forget_rating_offset,
|
||||
forget_session_len: simulator_config.forget_session_len,
|
||||
loss_aversion: simulator_config.loss_aversion,
|
||||
loss_aversion: 1.0,
|
||||
learn_limit: simulator_config.learn_limit as u32,
|
||||
review_limit: simulator_config.review_limit as u32,
|
||||
learning_step_transitions: simulator_config
|
||||
.learning_step_transitions
|
||||
.iter()
|
||||
.flatten()
|
||||
.cloned()
|
||||
.collect(),
|
||||
relearning_step_transitions: simulator_config
|
||||
.relearning_step_transitions
|
||||
.iter()
|
||||
.flatten()
|
||||
.cloned()
|
||||
.collect(),
|
||||
state_rating_costs: simulator_config
|
||||
.state_rating_costs
|
||||
.iter()
|
||||
.flatten()
|
||||
.cloned()
|
||||
.collect(),
|
||||
learning_step_count: simulator_config.learning_step_count as u32,
|
||||
relearning_step_count: simulator_config.relearning_step_count as u32,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
use fsrs::FSRS;
|
||||
use fsrs::FSRS5_DEFAULT_DECAY;
|
||||
|
||||
use crate::card::CardType;
|
||||
use crate::card::FsrsMemoryState;
|
||||
|
@ -37,10 +38,11 @@ impl Collection {
|
|||
let fsrs_retrievability = card
|
||||
.memory_state
|
||||
.zip(Some(days_elapsed))
|
||||
.map(|(state, days)| {
|
||||
.zip(Some(card.decay.unwrap_or(FSRS5_DEFAULT_DECAY)))
|
||||
.map(|((state, days), decay)| {
|
||||
FSRS::new(None)
|
||||
.unwrap()
|
||||
.current_retrievability(state.into(), days)
|
||||
.current_retrievability(state.into(), days, decay)
|
||||
});
|
||||
|
||||
let original_deck = if card.original_deck_id == DeckId(0) {
|
||||
|
@ -75,6 +77,7 @@ impl Collection {
|
|||
memory_state: card.memory_state.map(Into::into),
|
||||
fsrs_retrievability,
|
||||
custom_data: card.custom_data,
|
||||
fsrs_params: preset.fsrs_params().to_vec(),
|
||||
preset: preset.name,
|
||||
original_deck: if original_deck != deck {
|
||||
Some(original_deck.human_name())
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
use anki_proto::stats::graphs_response::Retrievability;
|
||||
use fsrs::FSRS;
|
||||
use fsrs::FSRS5_DEFAULT_DECAY;
|
||||
|
||||
use crate::prelude::TimestampSecs;
|
||||
use crate::scheduler::timing::SchedTimingToday;
|
||||
|
@ -30,7 +31,11 @@ impl GraphsContext {
|
|||
entry.1 += 1;
|
||||
if let Some(state) = card.memory_state {
|
||||
let elapsed_days = card.days_since_last_review(&timing).unwrap_or_default();
|
||||
let r = fsrs.current_retrievability(state.into(), elapsed_days);
|
||||
let r = fsrs.current_retrievability(
|
||||
state.into(),
|
||||
elapsed_days,
|
||||
card.decay.unwrap_or(FSRS5_DEFAULT_DECAY),
|
||||
);
|
||||
|
||||
*retrievability
|
||||
.retrievability
|
||||
|
|
|
@ -42,6 +42,11 @@ pub(crate) struct CardData {
|
|||
deserialize_with = "default_on_invalid"
|
||||
)]
|
||||
pub(crate) fsrs_desired_retention: Option<f32>,
|
||||
#[serde(
|
||||
skip_serializing_if = "Option::is_none",
|
||||
deserialize_with = "default_on_invalid"
|
||||
)]
|
||||
pub(crate) decay: Option<f32>,
|
||||
|
||||
/// A string representation of a JSON object storing optional data
|
||||
/// associated with the card, so v3 custom scheduling code can persist
|
||||
|
@ -57,6 +62,7 @@ impl CardData {
|
|||
fsrs_stability: card.memory_state.as_ref().map(|m| m.stability),
|
||||
fsrs_difficulty: card.memory_state.as_ref().map(|m| m.difficulty),
|
||||
fsrs_desired_retention: card.desired_retention,
|
||||
decay: card.decay,
|
||||
custom_data: card.custom_data.clone(),
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +93,9 @@ impl CardData {
|
|||
if let Some(v) = &mut self.fsrs_desired_retention {
|
||||
round_to_places(v, 2)
|
||||
}
|
||||
if let Some(v) = &mut self.decay {
|
||||
round_to_places(v, 3)
|
||||
}
|
||||
serde_json::to_string(&self).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -159,11 +168,12 @@ mod test {
|
|||
fsrs_stability: Some(123.45678),
|
||||
fsrs_difficulty: Some(1.234567),
|
||||
fsrs_desired_retention: Some(0.987654),
|
||||
decay: Some(0.123456),
|
||||
custom_data: "".to_string(),
|
||||
};
|
||||
assert_eq!(
|
||||
data.convert_to_json().unwrap(),
|
||||
r#"{"s":123.457,"d":1.235,"dr":0.99}"#
|
||||
r#"{"s":123.457,"d":1.235,"dr":0.99,"decay":0.123}"#
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@ fn row_to_card(row: &Row) -> result::Result<Card, rusqlite::Error> {
|
|||
original_position: data.original_position,
|
||||
memory_state: data.memory_state(),
|
||||
desired_retention: data.fsrs_desired_retention,
|
||||
decay: data.decay,
|
||||
custom_data: data.custom_data,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use std::sync::Arc;
|
|||
|
||||
use fnv::FnvHasher;
|
||||
use fsrs::FSRS;
|
||||
use fsrs::FSRS5_DEFAULT_DECAY;
|
||||
use regex::Regex;
|
||||
use rusqlite::functions::FunctionFlags;
|
||||
use rusqlite::params;
|
||||
|
@ -325,10 +326,11 @@ fn add_extract_fsrs_retrievability(db: &Connection) -> rusqlite::Result<()> {
|
|||
let review_day = due.saturating_sub(ivl);
|
||||
days_elapsed.saturating_sub(review_day) as u32
|
||||
};
|
||||
let decay = card_data.decay.unwrap_or_default();
|
||||
Ok(card_data.memory_state().map(|state| {
|
||||
FSRS::new(None)
|
||||
.unwrap()
|
||||
.current_retrievability(state.into(), days_elapsed)
|
||||
.current_retrievability(state.into(), days_elapsed, decay)
|
||||
}))
|
||||
},
|
||||
)
|
||||
|
@ -374,10 +376,11 @@ fn add_extract_fsrs_relative_retrievability(db: &Connection) -> rusqlite::Result
|
|||
{
|
||||
// avoid div by zero
|
||||
desired_retrievability = desired_retrievability.max(0.0001);
|
||||
let decay = card_data.decay.unwrap_or(FSRS5_DEFAULT_DECAY);
|
||||
|
||||
let current_retrievability = FSRS::new(None)
|
||||
.unwrap()
|
||||
.current_retrievability(state.into(), days_elapsed)
|
||||
.current_retrievability(state.into(), days_elapsed, decay)
|
||||
.max(0.0001);
|
||||
|
||||
return Ok(Some(
|
||||
|
|
|
@ -332,6 +332,7 @@ impl From<CardEntry> for Card {
|
|||
original_position: data.original_position,
|
||||
memory_state: data.memory_state(),
|
||||
desired_retention: data.fsrs_desired_retention,
|
||||
decay: data.decay,
|
||||
custom_data: data.custom_data,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
|
||||
$: fsrsEnabled = stats?.memoryState != null;
|
||||
$: desiredRetention = stats?.desiredRetention ?? 0.9;
|
||||
$: decay = (() => {
|
||||
const paramsLength = stats?.fsrsParams?.length ?? 0;
|
||||
if (paramsLength === 0) {
|
||||
return 0.2; // default decay for FSRS-6
|
||||
}
|
||||
if (paramsLength < 21) {
|
||||
return 0.5; // default decay for FSRS-4.5 and FSRS-5
|
||||
}
|
||||
return stats?.fsrsParams?.[20] ?? 0.2;
|
||||
})();
|
||||
</script>
|
||||
|
||||
<Container breakpoint="md" --gutter-inline="1rem" --gutter-block="0.5rem">
|
||||
|
@ -33,7 +43,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
{/if}
|
||||
{#if fsrsEnabled}
|
||||
<Row>
|
||||
<ForgettingCurve revlog={stats.revlog} {desiredRetention} />
|
||||
<ForgettingCurve revlog={stats.revlog} {desiredRetention} {decay} />
|
||||
</Row>
|
||||
{/if}
|
||||
{:else}
|
||||
|
|
|
@ -21,6 +21,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
|
||||
export let revlog: RevlogEntry[];
|
||||
export let desiredRetention: number;
|
||||
export let decay: number;
|
||||
let svg: HTMLElement | SVGElement | null = null;
|
||||
const bounds = defaultGraphBounds();
|
||||
const title = tr.cardStatsFsrsForgettingCurveTitle();
|
||||
|
@ -47,6 +48,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
svg as SVGElement,
|
||||
bounds,
|
||||
desiredRetention,
|
||||
decay,
|
||||
);
|
||||
</script>
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
import { filterRevlogEntryByReviewKind } from "./forgetting-curve";
|
||||
|
||||
export let revlog: RevlogEntry[];
|
||||
export let fsrsEnabled: boolean = false;
|
||||
export const fsrsEnabled: boolean = false;
|
||||
|
||||
function reviewKindClass(entry: RevlogEntry): string {
|
||||
switch (entry.reviewKind) {
|
||||
|
@ -174,7 +174,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{#if fsrsEnabled}{/if}
|
||||
<!-- {#if fsrsEnabled}
|
||||
<div class="column">
|
||||
<div class="column-head">{tr2.cardStatsFsrsStability()}</div>
|
||||
<div class="column-content right">
|
||||
{#each revlogRows as row, _index}
|
||||
<div>{row.stability}</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if} -->
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
|
|
@ -11,12 +11,11 @@ import { axisBottom, axisLeft, line, max, min, pointer, scaleLinear, scaleTime,
|
|||
import { type GraphBounds, setDataAvailable } from "../graphs/graph-helpers";
|
||||
import { hideTooltip, showTooltip } from "../graphs/tooltip-utils.svelte";
|
||||
|
||||
const FACTOR = 19 / 81;
|
||||
const DECAY = -0.5;
|
||||
const MIN_POINTS = 1000;
|
||||
|
||||
function forgettingCurve(stability: number, daysElapsed: number): number {
|
||||
return Math.pow((daysElapsed / stability) * FACTOR + 1.0, DECAY);
|
||||
function forgettingCurve(stability: number, daysElapsed: number, decay: number): number {
|
||||
const factor = Math.pow(0.9, 1 / -decay) - 1;
|
||||
return Math.pow((daysElapsed / stability) * factor + 1.0, -decay);
|
||||
}
|
||||
|
||||
interface DataPoint {
|
||||
|
@ -68,7 +67,7 @@ export function filterRevlog(revlog: RevlogEntry[]): RevlogEntry[] {
|
|||
return result.filter((entry) => filterRevlogEntryByReviewKind(entry));
|
||||
}
|
||||
|
||||
export function prepareData(revlog: RevlogEntry[], maxDays: number) {
|
||||
export function prepareData(revlog: RevlogEntry[], maxDays: number, decay: number) {
|
||||
const data: DataPoint[] = [];
|
||||
let lastReviewTime = 0;
|
||||
let lastStability = 0;
|
||||
|
@ -97,7 +96,7 @@ export function prepareData(revlog: RevlogEntry[], maxDays: number) {
|
|||
let elapsedDays = 0;
|
||||
while (elapsedDays < totalDaysElapsed - step) {
|
||||
elapsedDays += step;
|
||||
const retrievability = forgettingCurve(lastStability, elapsedDays);
|
||||
const retrievability = forgettingCurve(lastStability, elapsedDays, decay);
|
||||
data.push({
|
||||
date: new Date((lastReviewTime + elapsedDays * 86400) * 1000),
|
||||
daysSinceFirstLearn: data[data.length - 1].daysSinceFirstLearn + step,
|
||||
|
@ -128,7 +127,7 @@ export function prepareData(revlog: RevlogEntry[], maxDays: number) {
|
|||
let elapsedDays = 0;
|
||||
while (elapsedDays < totalDaysSinceLastReview - step) {
|
||||
elapsedDays += step;
|
||||
const retrievability = forgettingCurve(lastStability, elapsedDays);
|
||||
const retrievability = forgettingCurve(lastStability, elapsedDays, decay);
|
||||
data.push({
|
||||
date: new Date((lastReviewTime + elapsedDays * 86400) * 1000),
|
||||
daysSinceFirstLearn: data[data.length - 1].daysSinceFirstLearn + step,
|
||||
|
@ -138,7 +137,7 @@ export function prepareData(revlog: RevlogEntry[], maxDays: number) {
|
|||
});
|
||||
}
|
||||
daysSinceFirstLearn += totalDaysSinceLastReview;
|
||||
const retrievability = forgettingCurve(lastStability, totalDaysSinceLastReview);
|
||||
const retrievability = forgettingCurve(lastStability, totalDaysSinceLastReview, decay);
|
||||
data.push({
|
||||
date: new Date(now * 1000),
|
||||
daysSinceFirstLearn: daysSinceFirstLearn,
|
||||
|
@ -151,7 +150,7 @@ export function prepareData(revlog: RevlogEntry[], maxDays: number) {
|
|||
let previewDaysElapsed = 0;
|
||||
while (previewDaysElapsed < previewDays) {
|
||||
previewDaysElapsed += step;
|
||||
const retrievability = forgettingCurve(lastStability, elapsedDays + previewDaysElapsed);
|
||||
const retrievability = forgettingCurve(lastStability, elapsedDays + previewDaysElapsed, decay);
|
||||
data.push({
|
||||
date: new Date((now + previewDaysElapsed * 86400) * 1000),
|
||||
daysSinceFirstLearn: data[data.length - 1].daysSinceFirstLearn + step,
|
||||
|
@ -185,6 +184,7 @@ export function renderForgettingCurve(
|
|||
svgElem: SVGElement,
|
||||
bounds: GraphBounds,
|
||||
desiredRetention: number,
|
||||
decay: number,
|
||||
) {
|
||||
const svg = select(svgElem);
|
||||
const trans = svg.transition().duration(600) as any;
|
||||
|
@ -194,7 +194,7 @@ export function renderForgettingCurve(
|
|||
}
|
||||
const maxDays = calculateMaxDays(filteredRevlog, timeRange);
|
||||
|
||||
const data = prepareData(filteredRevlog, maxDays);
|
||||
const data = prepareData(filteredRevlog, maxDays, decay);
|
||||
|
||||
if (data.length === 0) {
|
||||
setDataAvailable(svg, false);
|
||||
|
|
|
@ -59,7 +59,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
$: computing = computingParams || checkingParams || computingRetention;
|
||||
$: defaultparamSearch = `preset:"${state.getCurrentNameForSearch()}" -is:suspended`;
|
||||
$: roundedRetention = Number($config.desiredRetention.toFixed(2));
|
||||
$: desiredRetentionWarning = getRetentionWarning(roundedRetention);
|
||||
$: desiredRetentionWarning = getRetentionWarning(
|
||||
roundedRetention,
|
||||
fsrsParams($config),
|
||||
);
|
||||
$: retentionWarningClass = getRetentionWarningClass(roundedRetention);
|
||||
|
||||
let computeRetentionProgress:
|
||||
|
@ -89,8 +92,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
reviewOrder: $config.reviewOrder,
|
||||
});
|
||||
|
||||
function getRetentionWarning(retention: number): string {
|
||||
const decay = -0.5;
|
||||
function getRetentionWarning(retention: number, params: number[]): string {
|
||||
const decay = params.length > 20 ? -params[20] : -0.5; // default decay for FSRS-4.5 and FSRS-5
|
||||
const factor = 0.9 ** (1 / decay) - 1;
|
||||
const stability = 100;
|
||||
const days = Math.round(
|
||||
|
@ -167,7 +170,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
: tr.deckConfigFsrsParamsNoReviews();
|
||||
setTimeout(() => alert(msg), 200);
|
||||
} else {
|
||||
$config.fsrsParams5 = resp.params;
|
||||
$config.fsrsParams6 = resp.params;
|
||||
}
|
||||
if (computeParamsProgress) {
|
||||
computeParamsProgress.current = computeParamsProgress.total;
|
||||
|
@ -322,9 +325,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
|
||||
<div class="ms-1 me-1">
|
||||
<ParamsInputRow
|
||||
bind:value={$config.fsrsParams5}
|
||||
bind:value={$config.fsrsParams6}
|
||||
defaultValue={[]}
|
||||
defaults={defaults.fsrsParams5}
|
||||
defaults={defaults.fsrsParams6}
|
||||
>
|
||||
<SettingTitle on:click={() => openHelpModal("modelParams")}>
|
||||
{tr.deckConfigWeights()}
|
||||
|
|
|
@ -178,7 +178,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
);
|
||||
}
|
||||
|
||||
let easyDayPercentages = [...$config.easyDaysPercentages];
|
||||
$: easyDayPercentages = [...$config.easyDaysPercentages];
|
||||
</script>
|
||||
|
||||
<div class="modal" class:show={shown} class:d-block={shown} tabindex="-1">
|
||||
|
|
|
@ -461,7 +461,9 @@ export async function commitEditing(): Promise<void> {
|
|||
}
|
||||
|
||||
export function fsrsParams(config: DeckConfig_Config): number[] {
|
||||
if (config.fsrsParams5) {
|
||||
if (config.fsrsParams6) {
|
||||
return config.fsrsParams6;
|
||||
} else if (config.fsrsParams5) {
|
||||
return config.fsrsParams5;
|
||||
} else {
|
||||
return config.fsrsParams4;
|
||||
|
|
|
@ -2384,9 +2384,9 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"caniuse-lite@npm:^1.0.30001431, caniuse-lite@npm:^1.0.30001524, caniuse-lite@npm:^1.0.30001669":
|
||||
version: 1.0.30001671
|
||||
resolution: "caniuse-lite@npm:1.0.30001671"
|
||||
checksum: 10c0/9bb81be7be641fdcdf4d3722b661d4204cc203a489c16080503a72b1605bd5c1061f8ae2452cc6c15d6957c818182824eb34e6569521051795f42cd14e844f99
|
||||
version: 1.0.30001714
|
||||
resolution: "caniuse-lite@npm:1.0.30001714"
|
||||
checksum: 10c0/b0e3372f018c5c177912f0282af98049057d83c80846293a4e3df728644a622db42a9e8971d6b7708d76e0fd4e9f6d5ce93802cf4e6818de80fdf371dc0f6a06
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
Loading…
Reference in a new issue