-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Easy days: revisited #3661
base: main
Are you sure you want to change the base?
Easy days: revisited #3661
Conversation
Could you write a simulation for current easy days design? Like this: https://github.com/open-spaced-repetition/easy-days-simulator/blob/main/notebook.ipynb Edit: nvm, I made one: Looks good to me. I will implement it in the helper add-on. Edit: I find a problem when I set Mon to normal and the rest to reduced: The PR's design will reduce too much reviews for reduced days. Here is the previous design's result: Edit: When I only set one weekday to reduced, the workload seems more reasonable: Compared with the previous design: In summary, the current design in this pull request will cause the ease of reduced days to fluctuate in relation to the number of reduced days configured. |
Let's say there are six reduced days and one normal day, and the workload of one reduced day is 0.5. The total workload of a week is 0.5 * 6 + 1 = 4. If today is reduced day, the total workload of the rest days is 4 - 0.5 = 3.5. The avg is 3.5 / 6 = 0.58. So today's workload (0.5) is related to the avg (0.58). Here is the code: easy_days_modifier = []
total_review_count = sum(review_cnts)
EASY_DAYS_NORMAL_LOAD = 1.0
EASY_DAYS_REDUCED_MODIFIER = 0.5
EASY_DAYS_MINIMUM_LOAD = 0.0001
date_percentages = [max(EASY_DAYS_MINIMUM_LOAD, easy_days_percentages[date.dayofweek % 7]) for date in possible_dates]
for date, review_count in zip(possible_dates, review_cnts):
if easy_days_percentages[date.dayofweek % 7] == 1:
easy_days_modifier.append(EASY_DAYS_NORMAL_LOAD)
elif easy_days_percentages[date.dayofweek % 7] == 0.5:
other_days_count_total = total_review_count - review_count;
other_days_count_avg = other_days_count_total / (len(possible_dates) - 1)
other_days_percentage_total = sum(date_percentages) - easy_days_percentages[date.dayofweek % 7]
other_days_percentage_avg = other_days_percentage_total / (len(possible_dates) - 1)
if review_count / EASY_DAYS_REDUCED_MODIFIER > other_days_count_avg / other_days_percentage_avg:
easy_days_modifier.append(EASY_DAYS_MINIMUM_LOAD)
else:
easy_days_modifier.append(EASY_DAYS_NORMAL_LOAD)
else:
easy_days_modifier.append(EASY_DAYS_MINIMUM_LOAD) It works well:
|
#3643 was something of a temporary fix. However, having a feature be effectively disabled and it still affecting scheduling to that degree hinted at a more fundamental problem.
I found that the existing system has two problems: it biases cards to the furthest day, and removes many reasonable days from the list of possible days to schedule to.
format for scheduler examples:
Take the (fixed by the above PR, but kept for illustration of the issue) case where all days were set to normal:
good -> good graduating interval
good -> easy graduating interval
In the first case, the latest day is heavily biased. And in the second, days that would be entirely reasonable to schedule to are zeroed out.
The case with a day marked as reduced fares similarly.
good -> good graduating interval
good -> easy graduating interval
When there is at least one day set as reduced it fares a bit better, but it still overpowers the existing fuzz by putting a very heavy bias on days with less cards (the existing load balancer already does this (though in a gentler manner) so fuzzing ends up being overbiased by this particular data point). For a feature that is about scheduling fewer cards on certain days it sure does a lot more than that.
In this PR I have a different approach. The easy days modifers are either 0.0 or 1.0. This effectively turns that day on or off as a possible day to schedule to.
Naturally, minimum is always a 0.0 and normal is always a 1.0. The issue is how to toggle between 0.0 and 1.0 for reduced days. Here I use a simple method:
If the amount of cards due on a reduced day is below half the mean of all other days in the fuzz range, it is 1.0. Otherwise it is 0.0.
(note: in practice, the minimum modifier will be a small value such as 0.0001 to make it a rare occurance and remove the need to add a variety of special cases in the implementation)
(also note: half the mean is flexible, I can see it make more sense in practice for it to be 0.4 instead)
Lets look at the same cases above, but with this new approach.
With all days as normal:
good -> good graduating interval
good -> easy graduating interval
nothing changes, as it should be.
And with a single day reduced:
good -> good graduating interval
good -> easy graduating interval
The first case favored the day with fewer cards scheduled to it, but not exessively so. The second day has a fairly even distribution which is expected as the amount of cards due is fairly even.
Other cases/contrived examples!
all days are minimum: the easy day factor is constant for all of them so fuzzing occurs as normal
all days are reduced: days under the threshold will be 1.0 with higher load days being 0.0, if they are all close it will have the minimum load factor for all of them so normal balancing occurs.
a case where two days are reduced:
in this case, the threshold of reduced 2 is 18.75 and reduced 4 is 20.625. reduced 2 is above the threshold but reduced 4 is below, so the easy days modifier is is:
[1.0, 0.0, 1.0, 1.0, 1.0]
all reduced days:
resulting modifier:
[0.0, 0.0, 0.0]
But since the
0.0
is actually0.0001
, the fuzzing can proceed as normal .If we add a normal day with 100 cards, we can see it will only schedule to the normal day.
resulting modifier:
[0.0, 0.0, 0.0, 1.0]
In this particular case, day4 would need 126 cards scheduled before a reduced day would even have the option of being scheduled to.
I would also like to convert the underlying configuration from being a series of floats to a series of enumerations of (
Normal
/Reduced
/Minimal
), but I am unsure it is worth the effort.