Skip to content

Commit

Permalink
Feat/allow parameters to be modified (#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
ishiko732 committed Apr 11, 2024
1 parent 9894ca4 commit 26d4c39
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 10 deletions.
60 changes: 60 additions & 0 deletions __tests__/algorithm.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {
DECAY,
default_enable_fuzz,
default_maximum_interval,
default_request_retention,
default_w,
FACTOR,
fsrs,
FSRS,
Expand Down Expand Up @@ -296,3 +299,60 @@ describe("FSRS apply_fuzz", () => {
expect(fuzzedInterval).toBeLessThanOrEqual(max_ivl);
});
});

describe("change Params", () => {
test("change FSRSParameters", () => {
const f = fsrs();
// I(r,s),r=0.9 then I(r,s)=s
expect(f.interval_modifier).toEqual(1);
expect(f.parameters).toEqual(generatorParameters());

const request_retention = 0.8;
const update_w = [
1.14, 1.01, 5.44, 14.67, 5.3024, 1.5662, 1.2503, 0.0028, 1.5489, 0.1763,
0.9953, 2.7473, 0.0179, 0.3105, 0.3976, 0.0, 2.0902,
];
f.parameters = generatorParameters({
request_retention: request_retention,
w: update_w,
enable_fuzz: true,
});
expect(f.parameters.request_retention).toEqual(request_retention);
expect(f.parameters.w).toEqual(update_w);
expect(f.parameters.enable_fuzz).toEqual(true);
expect(f.interval_modifier).toEqual(
f.calculate_interval_modifier(request_retention),
);

f.parameters.request_retention = default_request_retention;
expect(f.interval_modifier).toEqual(
f.calculate_interval_modifier(default_request_retention),
);

f.parameters.w = default_w;
expect(f.parameters.w).toEqual(default_w);

f.parameters.maximum_interval = 365;
expect(f.parameters.maximum_interval).toEqual(365);

f.parameters.enable_fuzz = default_enable_fuzz;
expect(f.parameters.enable_fuzz).toEqual(default_enable_fuzz);

f.parameters = {} // check default values
expect(f.parameters).toEqual(generatorParameters());

});

test("calculate_interval_modifier", () => {
const f = new FSRSAlgorithm(generatorParameters());
expect(f.interval_modifier).toEqual(
f.calculate_interval_modifier(default_request_retention),
);
expect(() => {
f.parameters.request_retention = 1.2;
}).toThrow("Requested retention rate should be in the range (0,1]");
expect(() => {
f.parameters.request_retention = -0.2;
}).toThrow("Requested retention rate should be in the range (0,1]");
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ts-fsrs",
"version": "3.5.3",
"version": "3.5.4",
"description": "ts-fsrs is a ES modules package based on TypeScript, used to implement the Free Spaced Repetition Scheduler (FSRS) algorithm. It helps developers apply FSRS to their flashcard applications, there by improving the user learning experience.",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
Expand Down
69 changes: 61 additions & 8 deletions src/fsrs/algorithm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,69 @@ export const DECAY: number = -0.5;
export const FACTOR: number = 19 / 81;

export class FSRSAlgorithm {
protected param: FSRSParameters;
private readonly intervalModifier: number;
protected param!: FSRSParameters;
protected intervalModifier!: number;
protected seed?: string;

constructor(param: Partial<FSRSParameters>) {
this.param = generatorParameters(param);
// Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45
// The formula used is : I(r,s)= (r^{\frac{1}{DECAY}-1}) \times \frac{s}{FACTOR}
this.intervalModifier =
(Math.pow(this.param.request_retention, 1 / DECAY) - 1) / FACTOR;
constructor(params: Partial<FSRSParameters>) {
this.param = new Proxy(
generatorParameters(params),
this.params_handler_proxy(),
);
this.intervalModifier = this.calculate_interval_modifier(
this.param.request_retention,
);
}

get interval_modifier(): number {
return this.intervalModifier;
}

/**
* Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45
* The formula used is: I(r,s) = (r^(1/DECAY) - 1) * s / FACTOR
* @param request_retention 0<request_retention<=1,Requested retention rate
*/
calculate_interval_modifier(request_retention: number): number {
if (request_retention <= 0 || request_retention > 1) {
throw new Error("Requested retention rate should be in the range (0,1]");
}
return +((Math.pow(request_retention, 1 / DECAY) - 1) / FACTOR).toFixed(8);
}

get parameters(): FSRSParameters {
return this.param;
}

set parameters(params: Partial<FSRSParameters>) {
this.update_parameters(params);
}

private params_handler_proxy(): ProxyHandler<FSRSParameters> {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const _this: FSRSAlgorithm = this;
return {
set: function (target, prop, value) {
if (prop === "request_retention" && Number.isFinite(value)) {
_this.intervalModifier = _this.calculate_interval_modifier(
Number(value),
);
}
// @ts-ignore
target[prop] = value;
return true;
},
};
}

private update_parameters(params: Partial<FSRSParameters>): void {
const _params = generatorParameters(params);
for (const key in _params) {
if (key in this.param) {
const paramKey = key as keyof FSRSParameters;
this.param[paramKey] = _params[paramKey] as never;
}
}
}

init_ds(s: SchedulingCard): void {
Expand Down
2 changes: 1 addition & 1 deletion src/fsrs/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const default_w = [
];
export const default_enable_fuzz = false;

export const FSRSVersion: string = "3.5.3";
export const FSRSVersion: string = "3.5.4";

export const generatorParameters = (
props?: Partial<FSRSParameters>,
Expand Down

0 comments on commit 26d4c39

Please sign in to comment.