xquercus / load-balanced-scheduler

GNU General Public License v3.0
10 stars 9 forks source link

[feature request] Start balancing when interval is higher than #2

Closed omega3 closed 5 years ago

omega3 commented 5 years ago

Generally, I find it very useful add-on, as well as the idea of "fuzz". But I wish I could set a given interval value from which add-on will actually start balancing. For example if interval is lower than 15 days it stop even Anki fuzz (Anki original balancing feature). When interval is > 15 it balances interval.

This is because I prefer to balance myself my workload by checking statistics and decide how many new cards I add. In the first period when a card is relatively fresh I prefer to strictly control when it will appear next time.

To me it is important whether I will see a card in 2 or 4 days. It is less important whether they will appear in 26 or 34 days.

You write in manual of your add-on about fuzz but from I read in anki documentation fuzz is related to easy answers.

Balancing relates more to: https://apps.ankiweb.net/docs/manual.html#due-counts-and-time-estimates

Anki additionally adds a small amount of random variation to the next due times, in order to prevent cards that were introduced together and always rated the same from always staying next to each other. This variation is not shown on the time estimates but will be applied after selecting the button.

So now I don't know if it is related somehow to your add-on. But for cards that were new and just enter learning process with small intervals it doesn't make much sense to me. The fact that I learn a bunch of new cards in a single day doesn't mean that they are related to each other. There are other ways to separate them - reposition for example.

If this is not related to your add-on and cannot be added as configuration option can you think about creating perhaps another separate add-on that can disable this random variation.

If not possible for you let me know and I will write about it on Anki support forum.

xquercus commented 5 years ago

To me it is important whether I will see a card in 2 or 4 days.

Nether stock Anki nor this add-on offer precise scheduling. It's not something I've heard of someone wanting but it shouldn't be difficult to implement. It's not something that belongs in this add-on though.

If you want precise scheduling for intervals < 15 the quick kludge is to add the following to the top of load_balanced_ivl:

if ivl < 15:
    return ivl

The elegant solution is to patch _fuzzIvlRange() directly placing the same check before Anki's stock code.

I haven't tested either of those but it should work.

You write in manual of your add-on about fuzz but from I read in anki documentation fuzz is related to easy answers.

As far as I'm aware Anki doesn't take difficulty into account when fuzzing intervals.

So now I don't know if it is related somehow to your add-on. But for cards that were new and just enter learning process with small intervals it doesn't make much sense to me. The fact that I learn a bunch of new cards in a single day doesn't mean that they are related to each other.

That would be something to take up with the Anki developers. They decide when the fuzz function gets called. All this add-on does is choose the interval "in the fuzz" that has the fewest number of cards.

omega3 commented 5 years ago

I don't know how to paste it properly.

def load_balanced_ivl(self, ivl):
    """Return the (largest) interval that has the least number of cards and falls within the 'fuzz'"""
    if ivl < 15:
    return ivl
    orig_ivl = int(ivl)

returns error

An add-on you installed failed to load. If problems persist, please go to the Tools>Add-ons menu, and disable or delete the add-on.

When loading 'Load Balanced Scheduler':
Traceback (most recent call last):
  File "aqt/addons.py", line 60, in loadAddons
  File "/home/kompik/.local/share/Anki2/addons21/208879074/__init__.py", line 1, in <module>
    from . import load_balanced_scheduler
  File "/home/kompik/.local/share/Anki2/addons21/208879074/load_balanced_scheduler.py", line 28
    if ivl < 15:
    ^
IndentationError: unexpected indent

and this

if ivl < 15:
return ivl
def load_balanced_ivl(self, ivl):

return

When loading 'Load Balanced Scheduler':
Traceback (most recent call last):
  File "aqt/addons.py", line 60, in loadAddons
  File "/home/kompik/.local/share/Anki2/addons21/208879074/__init__.py", line 1, in <module>
    from . import load_balanced_scheduler
  File "/home/kompik/.local/share/Anki2/addons21/208879074/load_balanced_scheduler.py", line 27
    return ivl
         ^
IndentationError: expected an indented block

Can you show me how to precisely paste it?

Sorry to trouble you.

xquercus commented 5 years ago

I can't promise anything right now but if I have time next week maybe I can put together an actual plugin. Time is a bit short right now as I'm packing for vacation.

It's difficult to explain so let's try something easier. If you want to make another attempt, delete the current load_balance_ivl function with the one below. It will entirely disable fuzzing and load balancing. The def load_balanced_ivl(self, ivl): needs to be aligned left and there needs to be 4 spaces between the left margin and return best_ivl. Copying exactly what's below should work.

def load_balanced_ivl(self, ivl):
    return best_ivl

Sorry I can't be more help right now. Maybe next week.

Oh, you will of course need to restart anki.

omega3 commented 5 years ago

I just paste it:

I still get error:

Debug info:
Anki 2.1.4 Python 3.6.2 Qt 5.9.2 PyQt 5.9
Platform: Linux
Flags: frz=True ao=True sv=1

Caught exception:
  File "aqt/reviewer.py", line 276, in onEnterKey
  File "aqt/reviewer.py", line 236, in _answerCard
  File "anki/sched.py", line 87, in answerCard
  File "anki/sched.py", line 808, in _answerRevCard
  File "anki/sched.py", line 854, in _rescheduleRev
  File "anki/sched.py", line 931, in _updateRevIvl
  File "anki/sched.py", line 936, in _adjRevIvl
  File "/home/kompik/.local/share/Anki2/addons21/208879074/load_balanced_scheduler.py", line 27, in load_balanced_ivl
    return best_ivl
<class 'UnboundLocalError'>: local variable 'best_ivl' referenced before assignment

Of course it is not urgent, so have a good time.

xquercus commented 5 years ago

Generally, I find it very useful add-on, as well as the idea of "fuzz". But I wish I could set a given interval value from which add-on will actually start balancing. For example if interval is lower than 15 days it stop even Anki fuzz (Anki original balancing feature). When interval is > 15 it balances interval.

Give this a try. Unzip and place the entire disable-fuzz directory with it's contents inside your add-on directory. Once you restart anki you should see it in your list of add-ons.

By default it will disable the fuzz function entirely. You can set MINIMUM_INTERVAL to configure the smallest interval that anki will start fuzzing. For example, if you want to disable fuzzing for intervals less than 15, set this to 15.

This should work Load Balanced Scheduler as well. That is, if you configure this add-on to not fuzz intervals smaller than 15 Load Balanced Scheduler will not balance them either.

Testing was brief. Let me know how it goes.

disable-fuzz.zip

omega3 commented 5 years ago

I can see this add-on as number 208879074.

With default:

MINIMUM_INTERVAL = -1
LOG_LEVEL = 1

def new_fuzzIvlRange(self, ivl):
    if MINIMUM_INTERVAL < 0:

No errors but fuzz is still present.

I set it to:

MINIMUM_INTERVAL = 30
LOG_LEVEL = 1

def new_fuzzIvlRange(self, ivl):
    if MINIMUM_INTERVAL < 30:

and seems to work but I need to do more testing.

Edit: I realized that after 1 month Anki shows intervals in month value so it is hard to precisely tell how many days and if there is a fuzz or not.

When I do more testing, tomorrow or even for a two or three days I will post as new comment so you will be notified.

ijgnd commented 5 years ago

I wonder why anyone wouldn't want to use load-balanced-scheduler ... I think it's an ingenious tweak.

@xquercus: Thanks for sharing the add-on "disable-fuzz.zip". I think I discovered two tiny mistakes.

# MINIMUM_INTERVAL -- The smallest interval for which Anki will start to "fuzz".  A value of -1 will
#                     disable "fuzzing" entirely.
(...)
MINIMUM_INTERVAL = -1  
(...)
if MINIMUM_INTERVAL < 0:
        log_info("fuzzing disabled\n")
        return orig_fuzzIvlRange(self, ivl)
(...)
orig_fuzzIvlRange = anki.sched.Scheduler._fuzzIvlRange

Since most people ignore debugging options (and I check the intervals in a customized version of "Extended stats during review" from inside Anki) I removed them to make the code more readable. I also changed the imports. This seems to work:

# to disable virtually all fuzzing set MINIMUM_INTERVAL to a high value
MINIMUM_INTERVAL = 9999

def new_fuzzIvlRange(self, ivl):
    if ivl < MINIMUM_INTERVAL:
        return [ivl, ivl]
    else:
        return orig_fuzzIvlRange(self, ivl)

def new_v2_fuzzIvlRange(self, ivl):
    if ivl < MINIMUM_INTERVAL:
        return [ivl, ivl]
    else:
        return orig_v2_fuzzIvlRange(self, ivl)

# Patch Anki 2.0 and Anki 2.1 default scheduler
from anki.sched import Scheduler
orig_fuzzIvlRange = Scheduler._fuzzIvlRange
Scheduler._fuzzIvlRange = new_fuzzIvlRange

# Patch Anki 2.1 experimental v2 scheduler
from anki import version
if version.startswith("2.1"):
    from anki.schedv2 import Scheduler as Scheduler2
    orig_v2_fuzzIvlRange = Scheduler2._fuzzIvlRange
    Scheduler2._fuzzIvlRange = new_v2_fuzzIvlRange

xquercus wrote:

The elegant solution is to patch _fuzzIvlRange()

This might be the way if you want more flexibility. Though it's dangerous if you make a mistake. Since _fuzzIvlRange is the same in schedV1 and schedV2 shouldn't one version suffice? This add-on in the current from gives you the built-in version of _fuzzIvlRange. But you can modify it, see the comments below.

from anki.sched import Scheduler
from anki import version
if version.startswith("2.1"):
    from anki.schedv2 import Scheduler as Scheduler2

def _fuzzIvlRange(self, ivl):
    if ivl < 2:  # if interval is 0 or 1 days
        return [1, 1]
    elif ivl == 2:
        return [2, 3]
    elif ivl < 7:
        fuzz = int(ivl*0.25)
    elif ivl < 30:
        fuzz = max(2, int(ivl*0.15))
    else:
        fuzz = max(4, int(ivl*0.05))
    # fuzz at least a day
    fuzz = max(fuzz, 1)
    return [ivl-fuzz, ivl+fuzz]

Scheduler._fuzzIvlRange = _fuzzIvlRange
Scheduler2._fuzzIvlRange = _fuzzIvlRange

Explanations for people who don't know python:

xquercus commented 5 years ago

Thanks for catching that bug and glad you got it working. I'm going to bow out of this thread but will leave it open.

omega3 commented 5 years ago

@ijgnd

I wonder why anyone wouldn't want to use load-balanced-scheduler ... I think it's an ingenious tweak.

I have already explained above. I want to control my intervals myself. Again: to me it is important whether I will see a card in 2 or 4 days. It is less important whether they will appear in 26 or 34 days. I can control my workload by changing how many new cards I add to the cue and checking statistics what I do frequently anyway.

Nobody forces anybody to use it if someone thinks it is a bad idea. And this add-on doesn't have to be used all the time, like the whole deck life. User could use it to know Anki better, to learn how to use intervals and other Anki values and how to adjust them to personal liking. I was inspired by this video that has recently been posted on reddit: "Guide to Anki Intervals and Learning Steps" https://www.youtube.com/watch?v=1XaJjbCSXT0

That's why I wanted to learn how I actually learn. To find out I need precisely control my intervals for a couple of days, maybe weeks. This is where this add-on is really useful.

I tested it today (the version from zip file) with about 150 cards that are at various state (Lapses, New, learning, review) and seems to me that it works just fine. I am very happy to have this add-on.

But a big thank you for testing and for your code. I will test it as well tomorrow.

Obviously @xquercus I am grateful for this add-on. Thank you.

xquercus commented 5 years ago

There is discussion on a more polished version here. I'm going to go ahead and close this issue but feel free to participate in the other topic.