Truncated Principal Variation

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

CheckersGuy
Posts: 273
Joined: Wed Aug 24, 2016 9:49 pm

Truncated Principal Variation

Post by CheckersGuy »

Hey guys,
I am using PVS and have found a with Problem with my PVS-Search.
I wanted to test PVS (without Transposition tables or any other pruning that could effect the PV) on some testPositions to see wheter i always get the full Principal-Variation. Turns out that this wasnt the case. I would get the full Principal Variation almost all of the time but it sometimes happend that I got a short PV.

Here is my implementation of PVS:

Code: Select all

public static int principal(CheckerBoard board, int alpha, int beta, int depth, int ply, PVLine cLine, boolean inPVLine, long endingTime) {

        final boolean silentPosition =board.isSilentPosition();
        if &#40;depth <= 0 || board.isTerminalState&#40;) || &#40;endingTime - System.currentTimeMillis&#40;)) <= 0&#41; &#123;
                return quiesceneSearch2&#40;board, alpha, beta, endingTime&#41;;
        &#125;

        final int sideToMove = &#40;board.color == -1&#41; ? 0 &#58; 1;
        final ArrayList<Move> sucessors = MGenerator.getMoves&#40;board&#41;;
        int bestScore = -10000000;
        final PVLine localPV = new PVLine&#40;depth&#41;;
        final int size=sucessors.size&#40;);

        Move currentBest = null;

        if &#40;size >= 1&#41; &#123;
            board.makeMove&#40;sucessors.get&#40;0&#41;);
            bestScore = -principal&#40;board, -beta, -alpha, depth -1, ply+1, localPV, inPVLine, endingTime&#41;;
            board.undoMove&#40;sucessors.get&#40;0&#41;);
            if &#40;bestScore > alpha&#41; &#123;
                currentBest = sucessors.get&#40;0&#41;;
                if &#40;bestScore >= beta&#41; &#123;
                    if &#40;silentPosition&#41; &#123;
                        killer&#91;1&#93;&#91;ply&#93; = killer&#91;0&#93;&#91;ply&#93;;
                        killer&#91;0&#93;&#91;ply&#93; = sucessors.get&#40;0&#41;;
                        history&#91;sideToMove&#93;&#91;sucessors.get&#40;0&#41;.getFrom&#40;)&#93;&#91;sucessors.get&#40;0&#41;.getTo&#40;)&#93; += depth * depth;
                    &#125;
                    return bestScore;
                &#125; else &#123;
                    history&#91;sideToMove&#93;&#91;sucessors.get&#40;0&#41;.getFrom&#40;)&#93;&#91;sucessors.get&#40;0&#41;.getTo&#40;)&#93; -= depth ;
                &#125;
                alpha = bestScore;
                cLine.line&#91;0&#93; = sucessors.get&#40;0&#41;;
                for &#40;int p = 1; p <localPV.line.length; p++) &#123;
                    cLine.line&#91;p&#93; = localPV.line&#91;p - 1&#93;;
                &#125;

            &#125;
        &#125;


        for &#40;int i = 1; i < size; i++) &#123;
            board.makeMove&#40;sucessors.get&#40;i&#41;);
            int score = -principal&#40;board, -alpha - 1, -alpha, depth - 1, ply + 1, localPV, false, endingTime&#41;;
            if &#40;score > alpha && score < beta&#41; &#123;
                score = -principal&#40;board, -beta, -alpha, depth - 1, ply+1, localPV, false, endingTime&#41;;
                if &#40;score > alpha&#41; &#123;
                    alpha = score;
                &#125;

            &#125;
            board.undoMove&#40;sucessors.get&#40;i&#41;);

            if &#40;score > bestScore&#41; &#123;
                currentBest = sucessors.get&#40;i&#41;;
                bestScore = score;
                if &#40;score >= beta&#41; &#123;
                    if &#40;silentPosition&#41; &#123;
                        killer&#91;1&#93;&#91;ply&#93; = killer&#91;0&#93;&#91;ply&#93;;
                        killer&#91;0&#93;&#91;ply&#93; = sucessors.get&#40;i&#41;;
                       history&#91;sideToMove&#93;&#91;sucessors.get&#40;i&#41;.getFrom&#40;)&#93;&#91;sucessors.get&#40;i&#41;.getTo&#40;)&#93; += depth*depth ;
                    &#125;
                    break;
                &#125; else &#123;
                   history&#91;sideToMove&#93;&#91;sucessors.get&#40;i&#41;.getFrom&#40;)&#93;&#91;sucessors.get&#40;i&#41;.getTo&#40;)&#93; -= depth ;
                &#125;
                cLine.line&#91;0&#93; = sucessors.get&#40;i&#41;;
                for &#40;int p = 1; p <localPV.line.length; p++) &#123;
                    cLine.line&#91;p&#93; = localPV.line&#91;p - 1&#93;;
                &#125;
            &#125;
        &#125;
        return bestScore;
    &#125;
I thought my way of collecting the PV was correct. Would appreciate if you could tell me whether I am wrong or not.

:D :D :D
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Truncated Principal Variation

Post by Sven »

Could you also show the code for PVLine?

Furthermore, unrelated to your question: in case of a terminal position or timeout you must not call the quiescence search but return immediately.
CheckersGuy
Posts: 273
Joined: Wed Aug 24, 2016 9:49 pm

Re: Truncated Principal Variation

Post by CheckersGuy »

Sven Schüle wrote:Could you also show the code for PVLine?

Furthermore, unrelated to your question: in case of a terminal position or timeout you must not call the quiescence search but return immediately.
Yeah. I should probably do that. The PVLine is nothing but an array of Move objects.

Code: Select all

public class PVLine&#123;
    Move&#91;&#93;line ;
    public PVLine&#40;)&#123;
        this.line = new Move&#91;30&#93;;
    &#125;

    public boolean equals&#40;Object m&#41;&#123;
        if&#40;m instanceof PVLine&#41;&#123;
            PVLine next =&#40;PVLine&#41;m;
            for&#40;int i=0;i<next.line.length;i++)&#123;
                if&#40;!line&#91;i&#93;.equals&#40;next.line&#91;i&#93;))
                    return false;
            &#125;
            return true;
        &#125;
        return false;
    &#125;


    public void clear&#40;)&#123;
        this.line =new Move&#91;this.line.length&#93;;
    &#125;


    public String toString&#40;)&#123;
        String strt="";
        for&#40;int i=0;i<line.length;i++)&#123;
            if&#40;line&#91;i&#93;==null&#41;
                break;

            strt+=line&#91;i&#93;+" | ";
        &#125;
        return strt;
    &#125;
&#125;
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Truncated Principal Variation

Post by Sven »

CheckersGuy wrote:
Sven Schüle wrote:Could you also show the code for PVLine?

Furthermore, unrelated to your question: in case of a terminal position or timeout you must not call the quiescence search but return immediately.
Yeah. I should probably do that. The PVLine is nothing but an array of Move objects.

[...]
PVLine looks correct, at least with respect to the problem you described.

I think the root cause may be in the condition "if (score > bestScore)" which you take as sufficient to update best move and PV. But the score needs to be inside the alpha-beta window to make the current move your new best move. This also interferes with already updating alpha some lines above, that should not be done at that point since you would lose the ability to correctly check whether you have a new PV.
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Truncated Principal Variation

Post by Sven »

Sven Schüle wrote:
CheckersGuy wrote:
Sven Schüle wrote:Could you also show the code for PVLine?

Furthermore, unrelated to your question: in case of a terminal position or timeout you must not call the quiescence search but return immediately.
Yeah. I should probably do that. The PVLine is nothing but an array of Move objects.

[...]
PVLine looks correct, at least with respect to the problem you described.

I think the root cause may be in the condition "if (score > bestScore)" which you take as sufficient to update best move and PV. But the score needs to be inside the alpha-beta window to make the current move your new best move. This also interferes with already updating alpha some lines above, that should not be done at that point since you would lose the ability to correctly check whether you have a new PV.
What does this line mean?

Code: Select all

final PVLine localPV = new PVLine&#40;depth&#41;;
In your PVLine code I do not see a constructor taking an integer argument. Is the line above meant to build a PVLine instance of length "depth" instead of 30?
CheckersGuy
Posts: 273
Joined: Wed Aug 24, 2016 9:49 pm

Re: Truncated Principal Variation

Post by CheckersGuy »

Sven Schüle wrote:
Sven Schüle wrote:
CheckersGuy wrote:
Sven Schüle wrote:Could you also show the code for PVLine?

Furthermore, unrelated to your question: in case of a terminal position or timeout you must not call the quiescence search but return immediately.
Yeah. I should probably do that. The PVLine is nothing but an array of Move objects.

[...]
PVLine looks correct, at least with respect to the problem you described.

I think the root cause may be in the condition "if (score > bestScore)" which you take as sufficient to update best move and PV. But the score needs to be inside the alpha-beta window to make the current move your new best move. This also interferes with already updating alpha some lines above, that should not be done at that point since you would lose the ability to correctly check whether you have a new PV.
What does this line mean?

Code: Select all

final PVLine localPV = new PVLine&#40;depth&#41;;
In your PVLine code I do not see a constructor taking an integer argument. Is the line above meant to build a PVLine instance of length "depth" instead of 30?
So I should check for

Code: Select all

 if&#40;score>alpha&#41;
right after checking for

Code: Select all

if&#40;score>=beta&#41; .
?

For some reason I didnt copy the whole class. Here is the corrected verion.

Code: Select all

public class PVLine&#123;
    Move&#91;&#93;line ;
    public PVLine&#40;)&#123;
        this.line = new Move&#91;30&#93;;
    &#125;

    public boolean equals&#40;Object m&#41;&#123;
        if&#40;m instanceof PVLine&#41;&#123;
            PVLine next =&#40;PVLine&#41;m;
            for&#40;int i=0;i<next.line.length;i++)&#123;
                if&#40;!line&#91;i&#93;.equals&#40;next.line&#91;i&#93;))
                    return false;
            &#125;
            return true;
        &#125;
        return false;
    &#125;


    public void clear&#40;)&#123;
        this.line =new Move&#91;this.line.length&#93;;
    &#125;


    public String toString&#40;)&#123;
        String strt="";
        for&#40;int i=0;i<line.length;i++)&#123;
            if&#40;line&#91;i&#93;==null&#41;
                break;

            strt+=line&#91;i&#93;+" | ";
        &#125;
        return strt;
    &#125;

    public PVLine&#40;int size&#41;&#123;
        this.line = new Move&#91;size&#93;;
    &#125;
&#125;
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Truncated Principal Variation

Post by Sven »

CheckersGuy wrote:So I should check for

Code: Select all

 if&#40;score>alpha&#41;
right after checking for

Code: Select all

if&#40;score>=beta&#41; .
?
Yes, maybe like this:

Code: Select all

if &#40;score > bestScore&#41; &#123;
    currentBest = sucessors.get&#40;i&#41;;
    bestScore = score;
    if &#40;score >= beta&#41; &#123;
        break;
    &#125;
    if &#40;score > alpha&#41; &#123;
        alpha = score;
        buildPV&#40;cLine, currentBest, localPV&#41;; // could be a function?
    &#125;
&#125;
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Truncated Principal Variation

Post by Sven »

In general your code to build a new PV is also unsafe since you rely on the PV of the child node (localPV) not having a greater length (which is the capacity in your case, not the actual PV length) than that of the parent node (cLine). That might break as soon as you introduce some extensions, but even without it I would not keep unsafe code. To overcome that problem you might as well add an explicit "size" member to PVLine, set it to 0 on construction and set it appropriately when building a new PV from current best move and "localPV". But even without that "optimization" you'd better do a correct bounds checking before assigning anything to cLine[p].
CheckersGuy
Posts: 273
Joined: Wed Aug 24, 2016 9:49 pm

Re: Truncated Principal Variation

Post by CheckersGuy »

Sven Schüle wrote:In general your code to build a new PV is also unsafe since you rely on the PV of the child node (localPV) not having a greater length (which is the capacity in your case, not the actual PV length) than that of the parent node (cLine). That might break as soon as you introduce some extensions, but even without it I would not keep unsafe code. To overcome that problem you might as well add an explicit "size" member to PVLine, set it to 0 on construction and set it appropriately when building a new PV from current best move and "localPV". But even without that "optimization" you'd better do a correct bounds checking before assigning anything to cLine[p].
I am going to add that as well.
At least, I am getting the full PV now. However, I tested a version, which used the PV for moveOrdering vs one that did not and saw virtually no difference.
I gotta Play Sherlock again :D
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Truncated Principal Variation

Post by Sven »

CheckersGuy wrote:
Sven Schüle wrote:In general your code to build a new PV is also unsafe since you rely on the PV of the child node (localPV) not having a greater length (which is the capacity in your case, not the actual PV length) than that of the parent node (cLine). That might break as soon as you introduce some extensions, but even without it I would not keep unsafe code. To overcome that problem you might as well add an explicit "size" member to PVLine, set it to 0 on construction and set it appropriately when building a new PV from current best move and "localPV". But even without that "optimization" you'd better do a correct bounds checking before assigning anything to cLine[p].
I am going to add that as well.
At least, I am getting the full PV now.
Good to read - so the initial problem of this thread is solved!
CheckersGuy wrote:However, I tested a version, which used the PV for moveOrdering vs one that did not and saw virtually no difference.
Which would indicate that you did not implement using the PV for move ordering correctly ... How did you do it? The principal() function you have shown has a parameter "inPVLine" but you didn't use it.