/// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="id"></param>
        /// <param name="nnEvaluator"></param>
        /// <param name="searchParams"></param>
        /// <param name="childSelectParams"></param>
        /// <param name="gameLimitManager"></param>
        /// <param name="paramsSearchExecutionModifier"></param>
        public GameEngineCeresInProcess(string id, NNEvaluatorDef evaluatorDef,
                                        ParamsSearch searchParams          = null,
                                        ParamsSelect childSelectParams     = null,
                                        IManagerGameLimit gameLimitManager = null,
                                        ParamsSearchExecutionModifier paramsSearchExecutionModifier = null) : base(id)
        {
            if (evaluatorDef == null)
            {
                throw new ArgumentNullException(nameof(evaluatorDef));
            }

            // Use default settings for search and select params if not specified.
            if (searchParams == null)
            {
                searchParams = new ParamsSearch();
            }
            if (childSelectParams == null)
            {
                childSelectParams = new ParamsSelect();
            }

            // Use default limit manager if not specified.
            if (gameLimitManager == null)
            {
                gameLimitManager = new ManagerGameLimitCeres();
            }

            ParamsSearchExecutionModifier = paramsSearchExecutionModifier;
            EvaluatorDef      = evaluatorDef;
            SearchParams      = searchParams;
            GameLimitManager  = gameLimitManager;
            ChildSelectParams = childSelectParams;
        }
Beispiel #2
0
        /// <summary>
        /// Runs a new search.
        /// </summary>
        /// <param name="nnEvaluators"></param>
        /// <param name="paramsSelect"></param>
        /// <param name="paramsSearch"></param>
        /// <param name="limitManager"></param>
        /// <param name="paramsSearchExecutionPostprocessor"></param>
        /// <param name="reuseOtherContextForEvaluatedNodes"></param>
        /// <param name="priorMoves"></param>
        /// <param name="searchLimit"></param>
        /// <param name="verbose"></param>
        /// <param name="startTime"></param>
        /// <param name="gameMoveHistory"></param>
        /// <param name="progressCallback"></param>
        /// <param name="possiblyUsePositionCache"></param>
        /// <param name="isFirstMoveOfGame"></param>
        public void Search(NNEvaluatorSet nnEvaluators,
                           ParamsSelect paramsSelect,
                           ParamsSearch paramsSearch,
                           IManagerGameLimit limitManager,
                           ParamsSearchExecutionModifier paramsSearchExecutionPostprocessor,
                           MCTSIterator reuseOtherContextForEvaluatedNodes,
                           PositionWithHistory priorMoves,
                           SearchLimit searchLimit, bool verbose,
                           DateTime startTime,
                           List <GameMoveStat> gameMoveHistory,
                           MCTSManager.MCTSProgressCallback progressCallback = null,
                           bool possiblyUsePositionCache = false,
                           bool isFirstMoveOfGame        = false)
        {
            searchLimit = AdjustedSearchLimit(searchLimit, paramsSearch);

            int maxNodes;

            if (MCTSParamsFixed.STORAGE_USE_INCREMENTAL_ALLOC)
            {
                // In this mode, we are just reserving virtual address space
                // from a very large pool (e.g. 256TB for Windows).
                // Therefore it is safe to reserve a very large block.
                maxNodes = (int)(1.1f * MCTSNodeStore.MAX_NODES);
            }
            else
            {
                if (searchLimit.SearchCanBeExpanded)
                {
                    throw new Exception("STORAGE_USE_INCREMENTAL_ALLOC must be true when SearchCanBeExpanded.");
                }

                if (searchLimit.Type != SearchLimitType.NodesPerMove)
                {
                    maxNodes = (int)searchLimit.Value + 5_000;
                }
                else
                {
                    throw new Exception("STORAGE_USE_INCREMENTAL_ALLOC must be true when using time search limits.");
                }
            }

            MCTSNodeStore store = new MCTSNodeStore(maxNodes, priorMoves);

            SearchLimit searchLimitToUse = ConvertedSearchLimit(priorMoves.FinalPosition, searchLimit, 0, 0,
                                                                paramsSearch, limitManager,
                                                                gameMoveHistory, isFirstMoveOfGame);

            Manager = new MCTSManager(store, reuseOtherContextForEvaluatedNodes, null, null,
                                      nnEvaluators, paramsSearch, paramsSelect,
                                      searchLimitToUse, paramsSearchExecutionPostprocessor, limitManager,
                                      startTime, null, gameMoveHistory, isFirstMoveOfGame);

            using (new SearchContextExecutionBlock(Manager.Context))
            {
                (BestMove, TimingInfo) = MCTSManager.Search(Manager, verbose, progressCallback, possiblyUsePositionCache);
            }
        }
Beispiel #3
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="store"></param>
        /// <param name="reuseOtherContextForEvaluatedNodes"></param>
        /// <param name="reusePositionCache"></param>
        /// <param name="reuseTranspositionRoots"></param>
        /// <param name="nnEvaluators"></param>
        /// <param name="searchParams"></param>
        /// <param name="childSelectParams"></param>
        /// <param name="searchLimit"></param>
        /// <param name="paramsSearchExecutionPostprocessor"></param>
        /// <param name="limitManager"></param>
        /// <param name="startTime"></param>
        /// <param name="priorManager"></param>
        /// <param name="gameMoveHistory"></param>
        /// <param name="isFirstMoveOfGame"></param>
        public MCTSManager(MCTSNodeStore store,
                           MCTSIterator reuseOtherContextForEvaluatedNodes,
                           PositionEvalCache reusePositionCache,
                           TranspositionRootsDict reuseTranspositionRoots,
                           NNEvaluatorSet nnEvaluators,
                           ParamsSearch searchParams,
                           ParamsSelect childSelectParams,
                           SearchLimit searchLimit,
                           ParamsSearchExecutionModifier paramsSearchExecutionPostprocessor,
                           IManagerGameLimit limitManager,
                           DateTime startTime,
                           MCTSManager priorManager,
                           List <GameMoveStat> gameMoveHistory,
                           bool isFirstMoveOfGame)
        {
            if (searchLimit.IsPerGameLimit)
            {
                throw new Exception("Per game search limits not supported");
            }

            StartTimeThisSearch                = startTime;
            RootNWhenSearchStarted             = store.Nodes.nodes[store.RootIndex.Index].N;
            ParamsSearchExecutionPostprocessor = paramsSearchExecutionPostprocessor;
            IsFirstMoveOfGame = isFirstMoveOfGame;
            SearchLimit       = searchLimit;


            // Make our own copy of move history.
            PriorMoveStats = new List <GameMoveStat>();
            if (gameMoveHistory != null)
            {
                PriorMoveStats.AddRange(gameMoveHistory);
            }

            // Possibly autoselect new optimal parameters
            ParamsSearchExecutionChooser paramsChooser = new ParamsSearchExecutionChooser(nnEvaluators.EvaluatorDef,
                                                                                          searchParams, childSelectParams, searchLimit);

            // TODO: technically this is overwriting the params belonging to the prior search, that's ugly (but won't actually cause a problem)
            paramsChooser.ChooseOptimal(searchLimit.EstNumNodes(50_000, false), paramsSearchExecutionPostprocessor); // TODO: make 50_000 smarter


            int estNumNodes = EstimatedNumSearchNodesForEvaluator(searchLimit, nnEvaluators);

            // Adjust the nodes estimate if we are continuing an existing search
            if (searchLimit.Type == SearchLimitType.NodesPerMove && RootNWhenSearchStarted > 0)
            {
                estNumNodes = Math.Max(0, estNumNodes - RootNWhenSearchStarted);
            }
            Context = new MCTSIterator(store, reuseOtherContextForEvaluatedNodes, reusePositionCache, reuseTranspositionRoots,
                                       nnEvaluators, searchParams, childSelectParams, searchLimit, estNumNodes);
            ThreadSearchContext = Context;

            TerminationManager = new MCTSFutilityPruning(this, searchLimit.SearchMoves);
            LimitManager       = limitManager;

            CeresEnvironment.LogInfo("MCTS", "Init", $"SearchManager created for store {store}", InstanceID);
        }
Beispiel #4
0
 /// <summary>
 /// Constructor.
 /// </summary>
 public GameEngineDefCeres(string id, NNEvaluatorDef evaluatorDef, ParamsSearch searchParams = null,
                           ParamsSearchExecutionModifier paramsSearchExecutionPostprocessor  = null,
                           ParamsSelect selectParams = null, IManagerGameLimit overrideTimeManager = null)
     : base(id)
 {
     EvaluatorDef = evaluatorDef;
     SearchParams = searchParams ?? new ParamsSearch();
     ParamsSearchExecutionPostprocessor = paramsSearchExecutionPostprocessor;
     SelectParams        = selectParams ?? new ParamsSelect();
     OverrideTimeManager = overrideTimeManager;
 }
Beispiel #5
0
 /// <summary>
 /// Constructor.
 /// </summary>
 public GameEngineDefCeres(string id, NNEvaluatorDef evaluatorDef, ParamsSearch searchParams = null,
                           ParamsSearchExecutionModifier paramsSearchExecutionPostprocessor  = null,
                           ParamsSelect selectParams = null, IManagerGameLimit overrideTimeManager = null)
     : base(id)
 {
     // Make a defensive clone of the EvaluatorDef so it will definitely not be shared.
     EvaluatorDef = ObjUtils.DeepClone(evaluatorDef);
     SearchParams = searchParams ?? new ParamsSearch();
     ParamsSearchExecutionPostprocessor = paramsSearchExecutionPostprocessor;
     SelectParams        = selectParams ?? new ParamsSelect();
     OverrideTimeManager = overrideTimeManager;
 }
Beispiel #6
0
 public static void DumpTimeManagerDifference(bool differentOnly, IManagerGameLimit timeManager1, IManagerGameLimit timeManager2)
 {
     if (differentOnly)
     {
         if (timeManager1 != timeManager2)
         {
             Console.WriteLine("TimeManager = " + (timeManager2 == null ? "null" : timeManager2.GetType().ToString()));
         }
     }
     else
     {
         Console.WriteLine("TimeManager1 = " + (timeManager1 == null ? "(default)" : timeManager1.GetType().ToString()));
         Console.WriteLine("TimeManager2 = " + (timeManager2 == null ? "(default)" : timeManager2.GetType().ToString()));
     }
 }
Beispiel #7
0
        SearchOnFEN(NNEvaluatorSet nnEvaluators,
                    ParamsSelect paramsChildSelect,
                    ParamsSearch paramsSearch,
                    IManagerGameLimit timeManager,
                    ParamsSearchExecutionModifier paramsSearchExecutionPostprocessor,
                    MCTSIterator reuseOtherContextForEvaluatedNodes,
                    string fen, string movesStr, SearchLimit searchLimit,
                    bool verbose = false,
                    MCTSManager.MCTSProgressCallback progressCallback = null,
                    bool possiblyEnablePositionCache    = false,
                    List <GameMoveStat> gameMoveHistory = null)
        {
            PositionWithHistory priorMoves = PositionWithHistory.FromFENAndMovesUCI(fen, movesStr);

            return(Search(nnEvaluators, paramsChildSelect, paramsSearch, timeManager, paramsSearchExecutionPostprocessor,
                          reuseOtherContextForEvaluatedNodes, priorMoves, searchLimit, verbose,
                          DateTime.Now, gameMoveHistory, progressCallback, possiblyEnablePositionCache));
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="id"></param>
        /// <param name="evaluatorDef"></param>
        /// <param name="searchParams"></param>
        /// <param name="childSelectParams"></param>
        /// <param name="gameLimitManager"></param>
        /// <param name="paramsSearchExecutionModifier"></param>
        /// <param name="logFileName"></param>
        public GameEngineCeresInProcess(string id, NNEvaluatorDef evaluatorDef,
                                        ParamsSearch searchParams          = null,
                                        ParamsSelect childSelectParams     = null,
                                        IManagerGameLimit gameLimitManager = null,
                                        ParamsSearchExecutionModifier paramsSearchExecutionModifier = null,
                                        string logFileName = null) : base(id)
        {
            if (evaluatorDef == null)
            {
                throw new ArgumentNullException(nameof(evaluatorDef));
            }

            // Use default settings for search and select params if not specified.
            if (searchParams == null)
            {
                searchParams = new ParamsSearch();
            }
            if (childSelectParams == null)
            {
                childSelectParams = new ParamsSelect();
            }

            // Use default limit manager if not specified.
            if (gameLimitManager == null)
            {
                gameLimitManager = new ManagerGameLimitCeres();
            }

            ParamsSearchExecutionModifier = paramsSearchExecutionModifier;
            EvaluatorDef      = evaluatorDef;
            SearchParams      = searchParams;
            GameLimitManager  = gameLimitManager;
            ChildSelectParams = childSelectParams;
            SearchLogFileName = logFileName;
            VerboseMoveStats  = CeresUserSettingsManager.Settings.VerboseMoveStats;

            if (!string.IsNullOrEmpty(CeresUserSettingsManager.Settings.SearchLogFile))
            {
                SearchLogFileName = CeresUserSettingsManager.Settings.SearchLogFile;
            }
        }
Beispiel #9
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="store"></param>
        /// <param name="nnParams"></param>
        /// <param name="searchParams"></param>
        /// <param name="childSelectParams"></param>
        /// <param name="priorMoves">if null, the prior moves are taken from the passed store</param>
        /// <param name="searchLimit"></param>
        public MCTSManager(MCTSNodeStore store,
                           MCTSIterator reuseOtherContextForEvaluatedNodes,
                           PositionEvalCache reusePositionCache,
                           TranspositionRootsDict reuseTranspositionRoots,
                           NNEvaluatorSet nnEvaluators,
                           ParamsSearch searchParams,
                           ParamsSelect childSelectParams,
                           SearchLimit searchLimit,
                           ParamsSearchExecutionModifier paramsSearchExecutionPostprocessor,
                           IManagerGameLimit timeManager,
                           DateTime startTime,
                           MCTSManager priorManager,
                           List <GameMoveStat> gameMoveHistory,
                           bool isFirstMoveOfGame)
        {
            StartTimeThisSearch                = startTime;
            RootNWhenSearchStarted             = store.Nodes.nodes[store.RootIndex.Index].N;
            ParamsSearchExecutionPostprocessor = paramsSearchExecutionPostprocessor;
            IsFirstMoveOfGame = isFirstMoveOfGame;

            PriorMoveStats = new List <GameMoveStat>();

            // Make our own copy of move history.
            if (gameMoveHistory != null)
            {
                PriorMoveStats.AddRange(gameMoveHistory);
            }

            // Possibly convert time limit per game into time for this move.
            if (searchLimit.IsPerGameLimit)
            {
                SearchLimitType type = searchLimit.Type == SearchLimitType.SecondsForAllMoves
                                                       ? SearchLimitType.SecondsPerMove
                                                       : SearchLimitType.NodesPerMove;
                float rootQ = priorManager == null ? float.NaN : (float)store.RootNode.Q;
                ManagerGameLimitInputs timeManagerInputs = new(store.Nodes.PriorMoves.FinalPosition,
                                                               searchParams, PriorMoveStats,
                                                               type, store.RootNode.N, rootQ,
                                                               searchLimit.Value, searchLimit.ValueIncrement,
                                                               float.NaN, float.NaN,
                                                               maxMovesToGo : searchLimit.MaxMovesToGo,
                                                               isFirstMoveOfGame : isFirstMoveOfGame);

                ManagerGameLimitOutputs timeManagerOutputs = timeManager.ComputeMoveAllocation(timeManagerInputs);
                SearchLimit = timeManagerOutputs.LimitTarget;
            }
            else
            {
                SearchLimit = searchLimit;
            }

            // Possibly autoselect new optimal parameters
            ParamsSearchExecutionChooser paramsChooser = new ParamsSearchExecutionChooser(nnEvaluators.EvaluatorDef,
                                                                                          searchParams, childSelectParams, searchLimit);

            // TODO: technically this is overwriting the params belonging to the prior search, that's ugly (but won't actually cause a problem)
            paramsChooser.ChooseOptimal(searchLimit.EstNumNodes(50_000), paramsSearchExecutionPostprocessor); // TODO: make 50_000 smarter


            int estNumNodes = EstimatedNumSearchNodesForEvaluator(searchLimit, nnEvaluators);

            // Adjust the nodes estimate if we are continuing an existing search
            if (searchLimit.Type == SearchLimitType.NodesPerMove && RootNWhenSearchStarted > 0)
            {
                estNumNodes = Math.Max(0, estNumNodes - RootNWhenSearchStarted);
            }
            Context = new MCTSIterator(store, reuseOtherContextForEvaluatedNodes, reusePositionCache, reuseTranspositionRoots,
                                       nnEvaluators, searchParams, childSelectParams, searchLimit, estNumNodes);
            ThreadSearchContext = Context;

            TerminationManager = new MCTSFutilityPruning(this, Context);
            LimitManager       = timeManager;

            CeresEnvironment.LogInfo("MCTS", "Init", $"SearchManager created for store {store}", InstanceID);
        }
Beispiel #10
0
        static GameEngine GetEngine(GameEngineUCISpec engineSpec, string suffix,
                                    NNEvaluatorDef evaluatorDef,
                                    ParamsSearch paramsSearch, ParamsSelect paramsSelect, IManagerGameLimit timeManager)
        {
            bool resetMovesBetweenMoves = !paramsSearch.TreeReuseEnabled;
            bool enableTranpsositions   = paramsSearch.Execution.TranspositionMode != TranspositionMode.None;

            // Create requested type of engine
            if (engineSpec == null)
            {
                return(new GameEngineCeresInProcess("Ceres_" + suffix, evaluatorDef, paramsSearch, paramsSelect, timeManager, null));
            }
            else if (engineSpec.Name == "LC0")
            {
                if (evaluatorDef == null)
                {
                    throw new Exception("EvaluatorDef must be specified when running LC0 engine");
                }

                // TODO: do we really want to emulate always here? probably not
                // WARNING: above.
                bool forceDisableSmartPruning = false;
                return(new GameEngineLC0("LZ0_" + suffix, evaluatorDef.Nets[0].Net.NetworkID,
                                         forceDisableSmartPruning, false,
                                         paramsSearch, paramsSelect, evaluatorDef,
                                         null, CeresUserSettingsManager.GetLC0ExecutableFileName()));
            }
            else
            {
                return(engineSpec.CreateEngine());
            }
        }
Beispiel #11
0
        /// <summary>
        /// Dump two sets of parameters, optionally showing only differences.
        /// </summary>
        public static void DumpParams(TextWriter writer, bool differentOnly,
                                      GameEngineUCISpec externalEngine1Spec, GameEngineUCISpec externalEngine2Spec,
                                      NNEvaluatorDef evaluatorDef1, NNEvaluatorDef evaluatorDef2,
                                      SearchLimit searchLimit1, SearchLimit searchLimit2,
                                      ParamsSelect selectParams1, ParamsSelect selectParams2,
                                      ParamsSearch searchParams1, ParamsSearch searchParams2,
                                      IManagerGameLimit timeManager1, IManagerGameLimit timeManager2,
                                      ParamsSearchExecution paramsSearchExecution1,
                                      ParamsSearchExecution paramsSearchExecution2)
        {
            writer.WriteLine("\r\n-----------------------------------------------------------------------");
            writer.WriteLine("ENGINE 1 Options Modifications from Default");

            if (evaluatorDef1 != null)
            {
                writer.WriteLine("Ceres Evaluator : " + evaluatorDef1.ToString());
            }
            else
            {
                writer.Write("External UCI : " + externalEngine1Spec);
            }

            writer.Write(ObjUtils.FieldValuesDumpString <SearchLimit>(searchLimit1, SearchLimit.NodesPerMove(1), differentOnly));
            //      writer.Write(ObjUtils.FieldValuesDumpString<NNEvaluatorDef>(Def.NNEvaluators1.EvaluatorDef, new ParamsNN(), differentOnly));
            writer.Write(ObjUtils.FieldValuesDumpString <ParamsSelect>(selectParams1, new ParamsSelect(), differentOnly));
            writer.Write(ObjUtils.FieldValuesDumpString <ParamsSearch>(searchParams1, new ParamsSearch(), differentOnly));
            DumpTimeManagerDifference(true, null, timeManager1);
            writer.Write(ObjUtils.FieldValuesDumpString <ParamsSearchExecution>(paramsSearchExecution1, new ParamsSearchExecution(), differentOnly));

            writer.WriteLine("\r\n-----------------------------------------------------------------------");
            writer.WriteLine("ENGINE 2 Options Modifications from Engine 1");
            bool evaluatorsDifferent = false;

            bool eval2TypeDifferent = (evaluatorDef1 == null) != (evaluatorDef2 == null);

            if (eval2TypeDifferent)
            {
                evaluatorsDifferent = true;
            }
            else
            {
                if (evaluatorDef1 != null)
                {
                    evaluatorsDifferent = evaluatorDef1.ToString() != evaluatorDef2.ToString();
                }
                else
                {
                    evaluatorsDifferent = externalEngine1Spec.ToString() != externalEngine2Spec.ToString();
                }
            }
            if (!differentOnly || evaluatorsDifferent)
            {
                if (evaluatorDef1 != null && evaluatorDef2 != null)
                {
                    writer.WriteLine("Ceres Evaluator : " + evaluatorDef2.ToString());
                }
                else
                {
                    writer.Write("External UCI : " + externalEngine2Spec);
                }
            }

            if (searchLimit2 != null)
            {
                writer.Write(ObjUtils.FieldValuesDumpString <SearchLimit>(searchLimit2, searchLimit1, differentOnly));
                //      writer.Write(ObjUtils.FieldValuesDumpString<NNEvaluatorDef>(Def.NNEvaluators1.EvaluatorDef, Def.NNEvaluators2.EvaluatorDef, differentOnly));
                writer.Write(ObjUtils.FieldValuesDumpString <ParamsSelect>(selectParams2, selectParams1, differentOnly));
                writer.Write(ObjUtils.FieldValuesDumpString <ParamsSearch>(searchParams2, searchParams1, differentOnly));
                DumpTimeManagerDifference(differentOnly, timeManager1, timeManager2);
                writer.Write(ObjUtils.FieldValuesDumpString <ParamsSearchExecution>(paramsSearchExecution2, paramsSearchExecution1, differentOnly));
            }
        }