BB+ wrote:Is this a fair description?
1. At depth 20, move X is best with score 0.55.
2. At depth 21, move X "fails low" (returns a score worse than the aspiration window, taken here as [0.35,0.75]) with score less than 0.35.
3. Move Y then fails high with score above 0.75. The aspiration window is then changed to [0.45,1.05] or something.
4. Move Y now "fails low" with a score less than 0.45, due to search instability, hash interaction, or other assorted general nonsense.
5. And all the other moves X,W,V,U,... also score less than 0.45, so there is a general "fail low" at the root node.
Between events #1 and #2, move X is "best". Between #2 and #3, I think it still has that label, even though the score has dropped. Between #3 and #4, move Y is best. Between #4 and #5 seems to the area of dispute (go with X or Y?). And maybe the situation after #5 is also disputed.
Some engines might alternatively try to resolve the score of move X in step #2 (not a great idea IMO) before going on to other moves, though I'm not sure this solves every problem.
That's the general idea. There are a ton of ways to handle fail lows.
1. Instantly relax alpha and re-search (assume this is the first move at depth N+1 for now). You get a true score before you go on. Now, at least, you know how bad this move is and you can make some sort of judgement call on how much extra time you are willing to spend to find a better move (score drop = -.4, maybe 2x? score drop = -3.5 maybe 10x or you are going to lose, etc.) Crafty does it this way.
2. Keep searching other moves to see if any of them will trip over the alpha bound and produce a good score. This way you avoid the extra time to find a score for the first move when you would then spend more time to find a move better. The down side is that you might run thru the entire set of ply=1 moves with no best move, and now you have to relax beta and start over. The good news is that it is generally more efficient to fail low than to fail high. To fail low just requires one move search at ply=2, for each root move.
I have tried both, over the years, many times. I keep going back to 1. I want to know what is going on, not blindly search to see if there is something better, only to have to repeat the whole thing with a dropped alpha which will blow up the TT hits that now have a useless bound.
There are other alternatives.
3. Stanback (Zarkov) suggested that when a move fails low, you make sure that the ply=2 refutation move is stuffed back into the hash table, and then you start over at iteration 1. That hash entry will make that move fail low on each iteration, and will therefore try to find the second-best move. But with much better (iterated search) move ordering. The danger is that if that hash entry gets overwritten, you enter a loop since the original best move will pop to the top, only to fail low on the same iteration it initially failed low on. I have this on my to-do list, because when the best move fails low, it wrecks move ordering. All those hash entries suddenly become worthless since the bound is altered.
I've tried an alternative but at the time decided to not use it. Namely, use John's idea, but remove the original fail-low move from the ply-1 move list and search again from iteration 1. now that move can't fail high. Once you get back to the iteration where it failed low, you can add it back in, but after the new best move, which we hope will not fail low here. If it does, we might be in a really pathological position where everything is going to fail low. And that is always the troublesome case. For example, everyone has seen a really "deep" position where when you start your search, you see a score of +1.5 or +3.0 because it appears you can pick up material. But on the last iteration, you see what your opponent saw and realize that you can't take that safely. And your score is going to drop back to the +/- .3 range. Which means _all_ moves will fail low. And you probably don't want to have to do N iterated searches to the same depth to realize that _all_ mvoes are failing low. That might be a time killer...
But the idea of searching at the root, after you have found a best move, and another move fails high, then you relax beta and re-search and it fails low, so you throw that fail high away, is simply ridiculous. The TT can cause this. pruning/reduction decisions based on alpha/beta can cause this. If the fail high is bad at the root, why isn't it bad everywhere else?