Exemplo n.º 1
0
        /// <summary>
        /// Worker method that actually computes and updates the statistic.
        /// </summary>
        private void DoSetEarlyStopMoveSecondaryFlags()
        {
            if (Manager.Root.NumChildrenExpanded == 0)
            {
                return;
            }

            float aggressiveness = Context.ParamsSearch.MoveFutilityPruningAggressiveness;

            if (aggressiveness > 1.0f)
            {
                throw new Exception("Ceres configuration error: maximum value of EarlyStopMoveSecondaryAggressiveness is 1.0.");
            }

            float MIN_BEST_N_FRAC_REQUIRED = ManagerChooseRootMove.MIN_FRAC_N_REQUIRED_MIN(Context);

            // Calibrate aggressiveness such that :
            //   - at maximum value of 1.0 we assume 50% visits go to second best move(s)
            //   - at reasonable default value 0.5 we assume 75% of visits to go second best move(s)
            float aggressivenessMultiplier = 1.0f / (1.0f - aggressiveness * 0.5f);

            int?numRemainingSteps = Manager.EstimatedNumVisitsRemaining();

            // Can't make any determiniation if we can't estimate how many steps left
            if (numRemainingSteps is null)
            {
                return;
            }

            int numStepsTotal = (int)(numRemainingSteps / Manager.FractionSearchRemaining);

            MCTSNode[] nodesSortedN = Root.ChildrenSorted(n => - n.N);
            MCTSNode   bestNNode    = nodesSortedN[0];

            int minN = Root.N / 5;// (int)(bestQNode.N * MIN_BEST_N_FRAC_REQUIRED);

            MCTSNode[] nodesSortedQ = Root.ChildrenSorted(n => n.N < minN ? int.MaxValue : (float)n.Q);
            MCTSNode   bestQNode    = nodesSortedQ[0];

            float bestQ = (float)bestQNode.Q;


            ManagerChooseRootMove      bestMoveChoser = new(Context.Root, false, Context.ParamsSearch.MLHBonusFactor);
            Span <MCTSNodeStructChild> children       = Root.Ref.Children;

            for (int i = 0; i < Root.Ref.NumChildrenExpanded; i++)
            {
                // Never shut down second best move unless the whole search is eligible to shut down
                if (nodesSortedN.Length > 1)
                {
                    MCTSNode secondBestMove   = Context.ParamsSearch.BestMoveMode == ParamsSearch.BestMoveModeEnum.TopN ? nodesSortedN[1] : nodesSortedQ[1];
                    bool     isSecondBestMove = children[i].Move == secondBestMove.PriorMove;
                    if (isSecondBestMove && !Context.ParamsSearch.FutilityPruningStopSearchEnabled)
                    {
                        Context.RootMovesArePruned[i] = false;
                        continue;
                    }
                }

                float earlyStopGapRaw;
                if (Context.ParamsSearch.BestMoveMode == ParamsSearch.BestMoveModeEnum.TopN)
                {
                    earlyStopGapRaw = nodesSortedN[0].N - children[i].N;
                }
                else
                {
                    int minNRequired = (int)(bestQNode.N * MIN_BEST_N_FRAC_REQUIRED);
                    earlyStopGapRaw = minNRequired - children[i].N;
                }

                float earlyStopGapAjusted = earlyStopGapRaw * aggressivenessMultiplier;
                bool  earlyStopSimple     = earlyStopGapRaw > numRemainingSteps;

                if (MCTSDiagnostics.DumpSearchFutilityShutdown &&
                    earlyStopSimple &&
                    !Context.RootMovesArePruned[i] &&
                    NumberOfNotShutdownChildren() < 3)
                {
                    Console.WriteLine();
                    Console.WriteLine($"\r\nShutdown {children[i].Move} [{children[i].N}] at root N  {Context.Root.N} with remaning {numRemainingSteps}"
                                      + $" due to raw gapN {earlyStopGapRaw} adusted to {earlyStopGapAjusted} in mode {Context.ParamsSearch.BestMoveMode} aggmult {aggressivenessMultiplier}");
                    DumpDiagnosticsMoveShutdown();
                }

                Context.RootMovesArePruned[i] = earlyStopSimple;
                // Console.WriteLine(i + $" EarlyStopMoveSecondary(simple) gap={gapToBest} adjustedGap={inflatedGap} remaining={numRemainingSteps} ");
            }


            // TODO: log this
            //Console.WriteLine($"{Context.RemainingTime,5:F2}sec remains at N={Root.N}, setting  minN to {minN} number still considered {count} " +
            //                  $"using EstimatedNPS {Context.EstimatedNPS} with nodes remaining {Context.EstimatedNumStepsRemaining()} " +
            //                  statsStr);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Worker method that actually computes and updates the statistic.
        /// </summary>
        private void DoSetEarlyStopMoveSecondaryFlags()
        {
            if (Manager.Root.NumChildrenExpanded == 0)
            {
                return;
            }

            float aggressiveness = Context.ParamsSearch.MoveFutilityPruningAggressiveness;

            if (aggressiveness >= 1.5f)
            {
                throw new Exception("Ceres configuration error: maximum value of EarlyStopMoveSecondaryAggressiveness is 1.5.");
            }

            float MIN_BEST_N_FRAC_REQUIRED = ManagerChooseRootMove.MIN_FRAC_N_REQUIRED_MIN;

            // Calibrate aggressiveness such that :
            //   - at maximum value of 1.0 we assume 50% visits go to second best move(s)
            //   - at reasonable default value 0.5 we assume 75% of visits to go second best move(s)
            float aggressivenessMultiplier = 1.0f / (1.0f - aggressiveness * 0.5f);

            int?numRemainingSteps = Manager.EstimatedNumVisitsRemaining();

            // Can't make any determiniation if we can't estimate how many steps left
            if (numRemainingSteps is null)
            {
                return;
            }

            int numStepsTotal = (int)(numRemainingSteps / Manager.FractionSearchRemaining);

            MCTSNode[] nodesSortedN = Root.ChildrenSorted(n => - n.N);
            MCTSNode   bestNNode    = nodesSortedN[0];

            int minN = Root.N / 5;// (int)(bestQNode.N * MIN_BEST_N_FRAC_REQUIRED);

            MCTSNode[] nodesSortedQ = Root.ChildrenSorted(n => n.N < minN ? int.MaxValue : (float)n.Q);
            MCTSNode   bestQNode    = nodesSortedQ[0];

            float bestQ = (float)bestQNode.Q;

            ManagerChooseRootMove      bestMoveChoser = new(Context.Root, false, Context.ParamsSearch.MLHBonusFactor);
            Span <MCTSNodeStructChild> children       = Root.Ref.Children;

            for (int i = 0; i < Root.Ref.NumChildrenExpanded; i++)
            {
                // Never shut down second best move unless the whole search is eligible to shut down
                if (nodesSortedN.Length > 1)
                {
                    MCTSNode secondBestMove   = Context.ParamsSearch.BestMoveMode == ParamsSearch.BestMoveModeEnum.TopN ? nodesSortedN[1] : nodesSortedQ[1];
                    bool     isSecondBestMove = children[i].Move == secondBestMove.PriorMove;
                    if (isSecondBestMove &&
                        !Context.ParamsSearch.FutilityPruningStopSearchEnabled &&
                        Context.RootMovesPruningStatus[i] != MCTSFutilityPruningStatus.PrunedDueToSearchMoves)
                    {
                        Context.RootMovesPruningStatus[i] = MCTSFutilityPruningStatus.NotPruned;
                        continue;
                    }
                }

                float earlyStopGapRaw;
                if (Context.ParamsSearch.BestMoveMode == ParamsSearch.BestMoveModeEnum.TopN)
                {
                    earlyStopGapRaw = nodesSortedN[0].N - children[i].N;
                }
                else
                {
                    int minNRequired = (int)(bestQNode.N * MIN_BEST_N_FRAC_REQUIRED);
                    earlyStopGapRaw = minNRequired - children[i].N;
                }

                float earlyStopGapAdjusted = earlyStopGapRaw * aggressivenessMultiplier;
                bool  earlyStop            = earlyStopGapAdjusted > numRemainingSteps;


                if (Context.RootMovesPruningStatus[i] == MCTSFutilityPruningStatus.NotPruned && earlyStop)
                {
#if NOT_HELPFUL
                    // Never shutdown nodes getting large fraction of all visits
                    float fracVisitsThisMoveRunningAverage = Context.RootMoveTracker != null ? Context.RootMoveTracker.RunningFractionVisits[i] : 0;

                    const float THRESHOLD_VISIT_FRACTION_DO_NOT_SHUTDOWN_CHILD = 0.20f;
                    bool        shouldVeto = (fracVisitsThisMoveRunningAverage > THRESHOLD_VISIT_FRACTION_DO_NOT_SHUTDOWN_CHILD);

                    if (Context.ParamsSearch.TestFlag &&
                        nodesSortedN[0] != Context.Root.ChildAtIndex(i) &&
                        NumberOfNotShutdownChildren() > 1 &&
                        shouldVeto)
                    {
                        MCTSEventSource.TestCounter1++;
                        continue;
                    }
                    else
#endif
                    Context.RootMovesPruningStatus[i] = MCTSFutilityPruningStatus.PrunedDueToFutility;
                    if (MCTSDiagnostics.DumpSearchFutilityShutdown)
                    {
                        Console.WriteLine();
                        Console.WriteLine($"\r\nShutdown {children[i].Move} [{children[i].N}] at root N  {Context.Root.N} with remaning {numRemainingSteps}"
                                          + $" due to raw gapN {earlyStopGapRaw} adusted to {earlyStopGapAdjusted} in mode {Context.ParamsSearch.BestMoveMode} aggmult {aggressivenessMultiplier}");
                        DumpDiagnosticsMoveShutdown();
                    }
                }
                // Console.WriteLine(i + $" EarlyStopMoveSecondary(simple) gap={gapToBest} adjustedGap={inflatedGap} remaining={numRemainingSteps} ");
            }


            // TODO: log this
            //Console.WriteLine($"{Context.RemainingTime,5:F2}sec remains at N={Root.N}, setting  minN to {minN} number still considered {count} " +
            //                  $"using EstimatedNPS {Context.EstimatedNPS} with nodes remaining {Context.EstimatedNumStepsRemaining()} " +
            //                  statsStr);
        }