Have you tried using verified null move only in the circumstances where you do not use null move at all currently ?bob wrote:In a private email discussion, someone asked if I had tested verified null-move on my cluster. I had not, because the idea made no sense to me and seemed to be nothing more than wasted search effort. But, I thought I would give it a whirl to see what happens.
The idea is that when a null-move search fails high, you do a normal search before you allow the NM fail high to terminate the search. This normal search is to a much-reduced depth. If the normal search fails low on the beta-1, beta bounds, then the null-move search fail high is ignored.
This can be limited in two ways.
1. How much do you reduce the depth on the verification search. I tried 3-4-5-6 plies. The bigger the number, the better the result, as this tends to reduce the extra effort expended.
2. Where do you do the verification searches. I limited it based on remaining depth so that it would not be done too near the tips to control effort expended. I tried this with remaining depth at least 3, then 4, ... and as before the bigger the value, the better the result.
Unfortunately, bigger values reduce the impact of the verification search, and when the depth limit (in 2 above) is large enough, a verification search is never done. The upper bound on this mess was the rating of the original program without the verification search. In other words, I could not get this idea to produce anything other than an Elo _loss_. Verified in my usual cluster-testing approach.
verified null move
Moderators: hgm, Rebel, chrisw
-
- Posts: 778
- Joined: Sat Jul 01, 2006 7:11 am
Re: verified null move
-
- Posts: 20943
- Joined: Mon Feb 27, 2006 7:30 pm
- Location: Birmingham, AL
Re: verified null move
No, because I am not aware of anyone that does null at all with zero pieces, and I use it everywhere else already.jwes wrote:Have you tried using verified null move only in the circumstances where you do not use null move at all currently ?bob wrote:In a private email discussion, someone asked if I had tested verified null-move on my cluster. I had not, because the idea made no sense to me and seemed to be nothing more than wasted search effort. But, I thought I would give it a whirl to see what happens.
The idea is that when a null-move search fails high, you do a normal search before you allow the NM fail high to terminate the search. This normal search is to a much-reduced depth. If the normal search fails low on the beta-1, beta bounds, then the null-move search fail high is ignored.
This can be limited in two ways.
1. How much do you reduce the depth on the verification search. I tried 3-4-5-6 plies. The bigger the number, the better the result, as this tends to reduce the extra effort expended.
2. Where do you do the verification searches. I limited it based on remaining depth so that it would not be done too near the tips to control effort expended. I tried this with remaining depth at least 3, then 4, ... and as before the bigger the value, the better the result.
Unfortunately, bigger values reduce the impact of the verification search, and when the depth limit (in 2 above) is large enough, a verification search is never done. The upper bound on this mess was the rating of the original program without the verification search. In other words, I could not get this idea to produce anything other than an Elo _loss_. Verified in my usual cluster-testing approach.
-
- Posts: 20943
- Joined: Mon Feb 27, 2006 7:30 pm
- Location: Birmingham, AL
Re: verified null move
After running the test as indicated, this is one of those changes that is at best, worth nothing, or drops a couple of Elo.
-
- Posts: 1154
- Joined: Fri Jun 23, 2006 5:18 am
Re: verified null move
This is something I spent a fair amount of time on a couple years ago. I have an odd null move (its depth adjusts based on many factors) and odd verification search, so my conclusions on this topic (as with most topics) may not be relevant. In my process of arriving at my null move / verification combo, I tried many many combinations of different null move / verification schemes. My testing was not Bobeske in its quality, but was quite thorough by any other standard. Since I have spent so much time on this, perhaps more than any other issue other than extensions/reductions/pruning, I should probably share my conclusions:
1. Most implementations of verification search hurt results slightly.
2. For most implementations of null move, it is possible with a lot of work to construct a set of circumstances for verification search where the net result is break-even, or in some cases a slight improvement.
3. The program plays more "aesthetic" with verification than without. That is, it avoids "obvious" errors in exchange for very slightly worse play at other times.
4. Verification is more important against humans than computer vs. computer, since it becomes relevant even with quite a few pieces on the board during "anti-computer" chess games that contain large locked pawn structures. Thus, almost all testing in this community underestimates the value of verification search as it applies to human vs. computer games.
Anyway, if you believe in this analysis, I think the action to take is based on which of the following camps you are in:
1. Keep program simple as possible. Only add complexity if it clearly strengthens the program.
2. Keep playing style as aesthetic as possible. Add any feature that makes the games more attractive if the do not clearly weaken the program.
I am in camp 2., so I use it.
-Sam
1. Most implementations of verification search hurt results slightly.
2. For most implementations of null move, it is possible with a lot of work to construct a set of circumstances for verification search where the net result is break-even, or in some cases a slight improvement.
3. The program plays more "aesthetic" with verification than without. That is, it avoids "obvious" errors in exchange for very slightly worse play at other times.
4. Verification is more important against humans than computer vs. computer, since it becomes relevant even with quite a few pieces on the board during "anti-computer" chess games that contain large locked pawn structures. Thus, almost all testing in this community underestimates the value of verification search as it applies to human vs. computer games.
Anyway, if you believe in this analysis, I think the action to take is based on which of the following camps you are in:
1. Keep program simple as possible. Only add complexity if it clearly strengthens the program.
2. Keep playing style as aesthetic as possible. Add any feature that makes the games more attractive if the do not clearly weaken the program.
I am in camp 2., so I use it.
-Sam
-
- Posts: 4565
- Joined: Sun Mar 12, 2006 2:40 am
- Full name:
Re: verified null move
Thanks for running the tests Bob, it is alway good to get some hard data.
The result seems to be what my more or less my impression was too, that verification searches may not help in elo but that it does not seem to hurt much either. The verification search is initially meant to prevent Zugzwang problems, so more cosmetic in nature I think?
It is maybe possible to do other variations but the variant I have tried is not really tested for elo. For instance you can do a very short verification search before nullmove, and if both it and nullmove are higher than beta only then do a longer verfication search. That is both a form of IID and a test for Zugzwang before doing nullmove I think. Also it is maybe possible to vary R based on the short verification search.
If you could get more answers from fail highs in standard nullwindow search than just whether result is equal or larger than beta you could use the same short verification before nullmove as a form of Futility pruning.
I must admit I have not really checked if my code for this ever gives good results in games, but for instance in Glaurung clone Ancalagon the code that is active at the moment does, or is supposed to be able to do some heavy futility pruning this way with the verification search on deep searches, even without testing what nullmove would return, at least that was my theory. But as I said I have not verified if it really helps or even yet if the code as it is now can actually work, other than in position tests.
Glaurung and Stockfish 1.3 code with some changes, probably not all sound as explained later:
The rest of nullmove is I believe still largely the same as in Stockfish 1.3.
I must admit I suspect this is only half working because the part with
will probably not generate results >= beta + Value(0x500)
I should change beta to beta + Value(0x500) for deep pruning to work but I also just would like to know if verificationsearchValue >= beta, because I'm also using that. Nullwindow seaches with a beta that is jumping around are probably not really what you want either. Because the deep pruning is only triggered if there is enough depth, this should not do anything in Blitz or in a short analysis. It is just an experimental thing for deep searches, any elo testing is only feasible at longer timecontrols. Not for using the verificationsearchValue >= beta in parallel with nullmove of course, that should be testable with your or other methods, only the deep pruning bit (brute pruning is the name in Blueberry Togas, there I believe I did use a higher beta to generate true beta + futility margin cutoffs)
Regards, Eelco
The result seems to be what my more or less my impression was too, that verification searches may not help in elo but that it does not seem to hurt much either. The verification search is initially meant to prevent Zugzwang problems, so more cosmetic in nature I think?
It is maybe possible to do other variations but the variant I have tried is not really tested for elo. For instance you can do a very short verification search before nullmove, and if both it and nullmove are higher than beta only then do a longer verfication search. That is both a form of IID and a test for Zugzwang before doing nullmove I think. Also it is maybe possible to vary R based on the short verification search.
If you could get more answers from fail highs in standard nullwindow search than just whether result is equal or larger than beta you could use the same short verification before nullmove as a form of Futility pruning.
I must admit I have not really checked if my code for this ever gives good results in games, but for instance in Glaurung clone Ancalagon the code that is active at the moment does, or is supposed to be able to do some heavy futility pruning this way with the verification search on deep searches, even without testing what nullmove would return, at least that was my theory. But as I said I have not verified if it really helps or even yet if the code as it is now can actually work, other than in position tests.
Glaurung and Stockfish 1.3 code with some changes, probably not all sound as explained later:
Code: Select all
// search() is the search function for zero-width nodes.
Value search(Position &pos, SearchStack ss[], Value beta, Depth depth,
int ply, bool allowNullmove, int threadID) {
assert(beta >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
assert(ply >= 0 && ply < PLY_MAX);
assert(threadID >= 0 && threadID < ActiveThreads);
if (depth < OnePly)
return qsearch(pos, ss, beta-1, beta, Depth(0), ply, threadID);
// Initialize, and make an early exit in case of an aborted search,
// an instant draw, maximum ply reached, etc.
init_node(ss, ply, threadID);
// After init_node() that calls poll()
if (AbortSearch || thread_should_stop(threadID))
return Value(0);
if (pos.is_draw())
return VALUE_DRAW;
EvalInfo ei;
if (ply >= PLY_MAX - 1)
return evaluate(pos, ei, threadID);
// Mate distance pruning
if (value_mated_in(ply) >= beta)
return beta;
if (value_mate_in(ply + 1) < beta)
return beta - 1;
Value approximateEval;
// Transposition table lookup
const TTEntry* tte = TT.retrieve(pos);
Move ttMove = (tte ? tte->move() : MOVE_NONE);
if (tte && ok_to_use_TT(tte, depth, beta, ply))
{
ss[ply].currentMove = ttMove; // can be MOVE_NONE
return value_from_tt(tte->value(), ply);
}
else
{
if (tte && tte->type() == VALUE_TYPE_EVAL)
approximateEval = tte->value();
else approximateEval = quick_evaluate(pos);
}
Value verificationsearchValue, nullValue;
int R; // Null move reduction
bool mateThreat = false;
bool isCheck = pos.is_check();
bool disableNull = !allowNullmove;
bool perpetualExtension = false;
// Null move search
if ( allowNullmove
&& depth > OnePly
&& !isCheck
&& !value_is_mate(beta)
&& ok_to_do_nullmove(pos)
&& approximateEval >= beta - NullMoveMargin)
{
ss[ply].currentMove = MOVE_NULL;
StateInfo st;
// Do an approximate search
verificationsearchValue = search(pos, ss, beta, depth-7*OnePly, ply, false, threadID);
// If the approximate search was at high enough depth and fails high five pawns predict a beta cutoff
if (depth >= 15 * OnePly)
{
if (verificationsearchValue >= beta + Value(0x500) && !value_is_mate(verificationsearchValue))
{
return beta;
}
}
pos.do_null_move(st);
if (depth >= 12 * OnePly)
{
if (verificationsearchValue >= beta + 0x100)
{
R = 5;
}
else if (verificationsearchValue <= beta - 0x100)
{
disableNull = true;
}
else R = 4;
}
else
{
R = (depth >= 5 * OnePly ? 4 : 3);
} // Null move dynamic reduction
if (!disableNull)
nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, true, threadID);
else nullValue = verificationsearchValue;
pos.undo_null_move();
if (value_is_mate(nullValue))
{
/* Do not return unproven mates */
}
else if (nullValue >= beta && verificationsearchValue >= beta)
{
if (depth < 6 * OnePly)
return beta;
// Do zugzwang verification search
verificationsearchValue = search(pos, ss, beta, depth-5*OnePly, ply, false, threadID);
if (verificationsearchValue >= beta)
return beta;
} else {
// The null move failed low, which means that we may be faced with
// some kind of threat. If the previous move was reduced, check if
// the move that refuted the null move was somehow connected to the
// move which was reduced. If a connection is found, return a fail
// low score (which will cause the reduced move to fail high in the
// parent node, which will trigger a re-search with full depth).
I must admit I suspect this is only half working because the part with
Code: Select all
// Do an approximate search
verificationsearchValue = search(pos, ss, beta, depth-7*OnePly, ply, false, threadID);
I should change beta to beta + Value(0x500) for deep pruning to work but I also just would like to know if verificationsearchValue >= beta, because I'm also using that. Nullwindow seaches with a beta that is jumping around are probably not really what you want either. Because the deep pruning is only triggered if there is enough depth, this should not do anything in Blitz or in a short analysis. It is just an experimental thing for deep searches, any elo testing is only feasible at longer timecontrols. Not for using the verificationsearchValue >= beta in parallel with nullmove of course, that should be testable with your or other methods, only the deep pruning bit (brute pruning is the name in Blueberry Togas, there I believe I did use a higher beta to generate true beta + futility margin cutoffs)
Regards, Eelco
Debugging is twice as hard as writing the code in the first
place. Therefore, if you write the code as cleverly as possible, you
are, by definition, not smart enough to debug it.
-- Brian W. Kernighan
place. Therefore, if you write the code as cleverly as possible, you
are, by definition, not smart enough to debug it.
-- Brian W. Kernighan
-
- Posts: 4565
- Joined: Sun Mar 12, 2006 2:40 am
- Full name:
Re: verified null move
For easier comparison with the Analagon code, the Stockfish 1.3 code of search() starts as follows:
Code: Select all
// search() is the search function for zero-width nodes.
Value search(Position &pos, SearchStack ss[], Value beta, Depth depth,
int ply, bool allowNullmove, int threadID) {
assert(beta >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
assert(ply >= 0 && ply < PLY_MAX);
assert(threadID >= 0 && threadID < ActiveThreads);
if (depth < OnePly)
return qsearch(pos, ss, beta-1, beta, Depth(0), ply, threadID);
// Initialize, and make an early exit in case of an aborted search,
// an instant draw, maximum ply reached, etc.
init_node(ss, ply, threadID);
// After init_node() that calls poll()
if (AbortSearch || thread_should_stop(threadID))
return Value(0);
if (pos.is_draw())
return VALUE_DRAW;
EvalInfo ei;
if (ply >= PLY_MAX - 1)
return evaluate(pos, ei, threadID);
// Mate distance pruning
if (value_mated_in(ply) >= beta)
return beta;
if (value_mate_in(ply + 1) < beta)
return beta - 1;
// Transposition table lookup
const TTEntry* tte = TT.retrieve(pos);
Move ttMove = (tte ? tte->move() : MOVE_NONE);
if (tte && ok_to_use_TT(tte, depth, beta, ply))
{
ss[ply].currentMove = ttMove; // can be MOVE_NONE
return value_from_tt(tte->value(), ply);
}
Value approximateEval = quick_evaluate(pos);
bool mateThreat = false;
bool isCheck = pos.is_check();
// Null move search
if ( allowNullmove
&& depth > OnePly
&& !isCheck
&& !value_is_mate(beta)
&& ok_to_do_nullmove(pos)
&& approximateEval >= beta - NullMoveMargin)
{
ss[ply].currentMove = MOVE_NULL;
StateInfo st;
pos.do_null_move(st);
int R = (depth >= 5 * OnePly ? 4 : 3); // Null move dynamic reduction
Value nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, false, threadID);
pos.undo_null_move();
if (value_is_mate(nullValue))
{
/* Do not return unproven mates */
}
else if (nullValue >= beta)
{
if (depth < 6 * OnePly)
return beta;
// Do zugzwang verification search
Value v = search(pos, ss, beta, depth-5*OnePly, ply, false, threadID);
if (v >= beta)
return beta;
} else {
// The null move failed low, which means that we may be faced with
// some kind of threat. If the previous move was reduced, check if
// the move that refuted the null move was somehow connected to the
// move which was reduced. If a connection is found, return a fail
// low score (which will cause the reduced move to fail high in the
// parent node, which will trigger a re-search with full depth).
Debugging is twice as hard as writing the code in the first
place. Therefore, if you write the code as cleverly as possible, you
are, by definition, not smart enough to debug it.
-- Brian W. Kernighan
place. Therefore, if you write the code as cleverly as possible, you
are, by definition, not smart enough to debug it.
-- Brian W. Kernighan