Example #1
0
        /// <summary>
        /// Startup method for Ceres UCI chess engine and supplemental features.
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
#if DEBUG
            Console.WriteLine();
            ConsoleUtils.WriteLineColored(ConsoleColor.Red, "*** WARNING: Ceres binaries built in Debug mode and will run much more slowly than Release");
#endif

            OutputBanner();
            CheckRecursiveOverflow();
            HardwareManager.VerifyHardwareSoftwareCompatability();

            // Load (or cause to be created) a settings file.
            if (!CeresUserSettingsManager.DefaultConfigFileExists)
            {
                Console.WriteLine();
                ConsoleUtils.WriteLineColored(ConsoleColor.Red, $"*** NOTE: Configuration file {CeresUserSettingsManager.DefaultCeresConfigFileName} not found in working directory.");
                Console.WriteLine();
                Console.WriteLine($"Prompting to for required values to initialize:");
                CeresUserSettingsManager.DoSetupInitialize();
            }

            // Configure logging level
            const bool LOG = false;
            CeresEnvironment.MONITORING_EVENTS = LOG;
            LogLevel    logLevel    = LOG ? LogLevel.Information : LogLevel.Critical;
            LoggerTypes loggerTypes = LoggerTypes.WinDebugLogger | LoggerTypes.ConsoleLogger;
            CeresEnvironment.Initialize(loggerTypes, logLevel);

            CeresEnvironment.MONITORING_METRICS = CeresUserSettingsManager.Settings.LaunchMonitor;

            //      if (CeresUserSettingsManager.Settings.DirLC0Networks != null)
            //        NNWeightsFilesLC0.RegisterDirectory(CeresUserSettingsManager.Settings.DirLC0Networks);

            MCTSEngineInitialization.BaseInitialize();


            Console.WriteLine();

#if DEBUG
            CheckDebugAllowed();
#endif

            if (args.Length > 0 && args[0].ToUpper() == "CUSTOM")
            {
                TournamentTest.Test(); return;
                //        SuiteTest.RunSuiteTest(); return;
            }

            StringBuilder allArgs = new StringBuilder();
            for (int i = 0; i < args.Length; i++)
            {
                allArgs.Append(args[i] + " ");
            }
            string allArgsString = allArgs.ToString();

            DispatchCommands.ProcessCommand(allArgsString);


            //  Win32.WriteCrashdumpFile(@"d:\temp\dump.dmp");
        }
Example #2
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());
            }
        }
Example #3
0
        public static void ProcessCommand(string cmd)
        {
            cmd = StringUtils.WhitespaceRemoved(cmd).TrimEnd();
            string[] parts = cmd.Split(" ");

            if (cmd == "")
            {
                // No arguments at all
                LaunchUCI("");
                Environment.Exit(0);
            }
            else if (parts.Length > 0 && parts[0].ToUpper() == "UCI")
            {
                // First argument explicit UCI
                LaunchUCI(cmd.Substring(cmd.IndexOf("UCI ") + 4));
                Environment.Exit(0);
            }
            else if (parts.Length > 0 && parts[0].Contains("="))
            {
                // No command, just some immediate key-value pairs
                LaunchUCI(cmd);
                Environment.Exit(0);
            }


            if (cmd.ToLower().EndsWith("-h") || cmd.ToLower().EndsWith("help"))
            {
                HelpCommands.ProcessHelpCommand(cmd);
            }

            // Command consists command name, then options (sequence of key=value pairs), sometimes followed by a FEN
            (string featureName, string args) = SplitLeft(cmd);
            string keyValueArgs = "";
            string fen          = null;

            // Default feature (if not specified) is UCI
            if (featureName == null)
            {
                featureName = "UCI";
            }

            featureName = featureName.ToUpper();

            // Separate key/value pairs and FEN (if any)
            if ((featureName == "ANALYZE" || featureName == "TOURN") && args != "")
            {
                int posLastEquals = args.LastIndexOf("=");
                if (posLastEquals == -1)
                {
                    fen = args;
                }
                else
                {
                    int indexEndValue = args.Substring(posLastEquals).IndexOf(" ");
                    if (indexEndValue != -1)
                    {
                        fen          = args.Substring(1 + posLastEquals + indexEndValue);
                        keyValueArgs = args.Substring(0, posLastEquals + indexEndValue);
                    }
                    else
                    {
                        keyValueArgs = args;
                    }
                }
            }
            else
            {
                keyValueArgs = args;
            }

            if (fen != null && fen.ToLower() == "startpos")
            {
                fen = Position.StartPosition.FEN;
            }

            // Extract the feature name

            // Extract and parse options string as sequence of key=value pairs
            //string options = keyValueArgs.Contains(" ") ? keyValueArgs.Substring(keyValueArgs.IndexOf(" ") + 1) : "";
            //KeyValueSetParsed keyValues =  options == "" ? null : new KeyValueSetParsed(options);

            if (featureName == "SUITE")
            {
                FeatureSuiteParams suiteParams = FeatureSuiteParams.ParseSuiteCommand(fen, keyValueArgs);
                FeatureSuiteParams.RunSuiteTest(suiteParams);
            }
            else if (featureName == "SETOPT")
            {
                if (!keyValueArgs.Contains("="))
                {
                    SetoptError();
                }

                KeyValueSetParsed keyValues = new KeyValueSetParsed(keyValueArgs, null);

                foreach ((string key, string value) in keyValues.KeyValuePairs)
                {
                    string keyLower = key.ToLower();
                    if (keyLower == "network")
                    {
                        CeresUserSettingsManager.Settings.DefaultNetworkSpecString = value;
                    }
                    else if (keyLower == "device")
                    {
                        CeresUserSettingsManager.Settings.DefaultDeviceSpecString = value;
                    }
                    else if (keyLower == "dir-epd")
                    {
                        CeresUserSettingsManager.Settings.DirEPD = value;
                    }
                    else if (keyLower == "dir-pgn")
                    {
                        CeresUserSettingsManager.Settings.DirPGN = value;
                    }
                    else if (keyLower == "dir-lc0-networks")
                    {
                        CeresUserSettingsManager.Settings.DirLC0Networks = value;
                    }
                    else if (keyLower == "dir-tablebases")
                    {
                        CeresUserSettingsManager.Settings.DirTablebases = value;
                    }
                    else if (keyLower == "launch-monitor")
                    {
                        CeresUserSettingsManager.Settings.LaunchMonitor = bool.Parse(value);
                    }
                    else if (keyLower == "log-info")
                    {
                        CeresUserSettingsManager.Settings.LogInfo = bool.Parse(value);
                    }
                    else if (keyLower == "log-warn")
                    {
                        CeresUserSettingsManager.Settings.LogWarn = bool.Parse(value);
                    }
                    else
                    {
                        SetoptError();
                    }

                    Console.WriteLine($"Set {key} to {value}");
                }

                Console.WriteLine($"Updating default Ceres settings file {CeresUserSettingsManager.DefaultCeresConfigFileName}");
                CeresUserSettingsManager.SaveToDefaultFile();
            }
            else if (featureName == "TOURN")
            {
                FeatureTournParams tournParams = FeatureTournParams.ParseTournCommand(fen, keyValueArgs);
                FeatureTournParams.RunTournament(tournParams, fen);
                Console.WriteLine(tournParams.ToString());
            }
            else if (featureName == "ANALYZE")
            {
                FeatureAnalyzeParams analyzeParams = FeatureAnalyzeParams.ParseAnalyzeCommand(fen, keyValueArgs);
                analyzeParams.Execute(fen);
            }
            else if (featureName == "SETUP")
            {
                if (File.Exists(CeresUserSettingsManager.DefaultCeresConfigFileName))
                {
                    Console.WriteLine();
                    ConsoleUtils.WriteLineColored(ConsoleColor.Red, "WARNING: This action will overwrite the Ceres.json file in the current directory.");
                    Console.WriteLine();
                }

                CeresUserSettingsManager.DoSetupInitialize();
            }
            else if (featureName == "SYSBENCH")
            {
                FeatureBenchmark.DumpBenchmark();
            }
            else
            {
                ShowErrorExit("Expected argument to begin with one of the features UCI, ANALYZE, SUITE, TOURN, SYSBENCH or SETOPT");
            }
        }
        /// <summary>
        /// Returns the executable location and program arguments appropriate
        /// for LC0 given specified Ceres parameters
        /// (optionally emulating them to the degree possible).
        /// </summary>
        /// <param name="paramsSearch"></param>
        /// <param name="paramsSelect"></param>
        /// <param name="evaluatorDef"></param>
        /// <param name="network"></param>
        /// <param name="emulateCeresOptions"></param>
        /// <param name="verboseOutput"></param>
        /// <param name="overrideEXE"></param>
        /// <param name="forceDisableSmartPruning"></param>
        /// <param name="alwaysFillHistory"></param>
        /// <returns></returns>
        public static (string, string) GetLC0EngineOptions(ParamsSearch paramsSearch,
                                                           ParamsSelect paramsSelect,
                                                           NNEvaluatorDef evaluatorDef,
                                                           INNWeightsFileInfo network,
                                                           bool emulateCeresOptions,
                                                           bool verboseOutput,
                                                           string overrideEXE            = null,
                                                           bool forceDisableSmartPruning = false,
                                                           bool alwaysFillHistory        = false)
        {
            if (paramsSearch == null)
            {
                paramsSearch = new ParamsSearch();
            }
            if (paramsSelect == null)
            {
                paramsSelect = new ParamsSelect();
            }

            //fail int8  string precisionStr = MCTSParams.PRECISION == WFEvalNetTensorRT.TRTPrecision.Int8 ? "trt-int8" : "cudnn-fp16";

            // Must reverse values to conform to LZ0 convention
            float FPU_MULTIPLIER = paramsSelect.FPUMode == ParamsSelect.FPUType.Reduction ? -1.0f : 1.0f;

            // TODO: plug in both versions of Centiapwn to low level Leela code
            string fpuVals     = $"--fpu-value={FPU_MULTIPLIER * paramsSelect.FPUValue} --fpu-value-at-root={FPU_MULTIPLIER * paramsSelect.FPUValueAtRoot} ";
            string strategyStr = paramsSelect.FPUMode == ParamsSelect.FPUType.Absolute ? "absolute " : "reduction ";
            string fpuStr      = fpuVals + "--fpu-strategy=" + strategyStr;

            string strategyAtRootStr = paramsSelect.FPUModeAtRoot == ParamsSelect.FPUType.Absolute ? "absolute " : "reduction ";
            string fpuStrRoot        = paramsSelect.FPUModeAtRoot == ParamsSelect.FPUType.Same ? "--fpu-strategy-at-root=same "
                                                                          : "--fpu-strategy-at-root=" + strategyAtRootStr;

            string netSourceFile = network.FileName;

            int minibatchSize = 256; // LC0 default

            // If GPUs have equal fractions then we use demux where only 2 threads needed,
            // otherwise we use roundrobin and best is 1 + number of GPUS
            // Note that with increasing threads Leela plays significantly non-deterministically and somewhat worse
            int NUM_THREADS = evaluatorDef.EqualFractions ? 2 : evaluatorDef.Devices.Length + 1;

            string lzOptions = "--nodes-as-playouts "; // essential to get same behavior as Ceres with go nodes command

            lzOptions += "--multi-gather ";            // greatly improves search speed

            if (forceDisableSmartPruning || (emulateCeresOptions && !paramsSearch.FutilityPruningStopSearchEnabled))
            {
                lzOptions += " --smart-pruning-factor=0 ";
            }

            // Default nncache is only 200_000 but big tournaments (TCEC 19) have used as high as 20_000_000.
            // To keep memory requires reasonable for typical systems we default to a value in between.
            // However note that for very small nets such as 128x10 it may be faster to uze zero nncache.
            const int LC0_CACHE_SIZE = 5_000_000;

            int MOVE_OVERHEAD = (int)(new ParamsSearch().MoveOverheadSeconds * 1000);

            lzOptions += $"--move-overhead={MOVE_OVERHEAD} ";
            if (USE_LC0_SMALL_SEARCH_SETTINGS)
            {
                // Works better for smaller searchs (such as 1000 nodes)
                lzOptions += " --max-collision-visits=32 ";
            }
            else
            {
                // Much faster for large searches with multigather enabled.
                lzOptions += " --max-out-of-order-evals-factor=2.4 --max-collision-events=500 --max-collision-visits=500 ";
            }

            if (alwaysFillHistory)
            {
                lzOptions += $" --history-fill=always ";
            }

            if (emulateCeresOptions)
            {
                bool useNNCache = evaluatorDef.CacheMode > PositionEvalCache.CacheMode.None ||
                                  paramsSearch.Execution.TranspositionMode > TranspositionMode.None;
                int cacheSize = useNNCache ? LC0_CACHE_SIZE : 0;

                lzOptions += $@"-w {netSourceFile} -t {NUM_THREADS} --minibatch-size={minibatchSize} " +
                             //        " --policy-softmax-temp=2.2  --backend=cudnn-fp16 ";
                             $" --policy-softmax-temp={paramsSelect.PolicySoftmax} --cache-history-length={evaluatorDef.NumCacheHashPositions - 1} " +
//                         $" --score-type=win_percentage" +
                             BackendArgumentsString(evaluatorDef) +
                             //"--backend=multiplexing --backend-opts=(backend=cudnn-fp16,gpu=0),(backend=cudnn-fp16,gpu=1),(backend=cudnn-fp16,gpu=2),(backend=cudnn-fp16,gpu=3) " +
                             //                         $"--backend={precisionStr} --backend-opts=gpu={SearchParamsNN.GPU_ID_LEELA_UCI} " +

                             $"{fpuStr} {fpuStrRoot} " +
                             $" --no-sticky-endgames ";

                // + --no-out-of-order-eval"; // *** NOTE: if we add this flag, LZ0 seems to play a little different and better. TODO: study this, should we adopt?
                lzOptions += $" --cpuct-factor={paramsSelect.CPUCTFactor} --cpuct-base={paramsSelect.CPUCTBase} --cpuct={paramsSelect.CPUCT} --nncache={cacheSize} ";
                //        lzOptions += $" --max-collision-visits={paramsSearch.MAX_COLLISIONS + 1 }"; // Must increment by 1 to make comparable (also, LC0 hangs at value ot zero)
            }
            else
            {
                // Mostly we let Leela use default options, except to make it fair
                // we use a large nncache and number of threads appropriate for the number of GPUs in use
                //        lzOptions = $@"-w {weightsDir}\{netSourceFile} --minibatch-size={minibatchSize} -t {paramsNN.NNEVAL_NUM_GPUS + 1} " +
                lzOptions += $@"-w {netSourceFile} -t {NUM_THREADS} " +
//                    $"--score-type=win_percentage " +
                             $"--nncache={LC0_CACHE_SIZE} " +
                             // like TCEC 10, only 5% benefit     $"--max-prefetch=160 --max-collision-events=917 " +
                             BackendArgumentsString(evaluatorDef);
            }

            string tbPath = CeresUserSettingsManager.Settings.TablebaseDirectory;

            if (paramsSearch.EnableTablebases)
            {
                lzOptions += (@$ " --syzygy-paths=#{tbPath}# ").Replace("#", "\"");
            }

            if (verboseOutput)
            {
                lzOptions += " --verbose-move-stats ";
            }

            string EXE = CeresUserSettingsManager.GetLC0ExecutableFileName();

#if EXPERIMENTAL
            const bool LZ_USE_TRT = false; // NOTE: if true, the GPU is seemingly currently hardcoded to 3. The max batch size is 512

            if (LZ_USE_TRT)
            {
                if (network.NetworkID == "59999")
                {
                    EXE = @"C:\dev\lc0\19May\lc0\build\lc0_59999.exe";
                }
                else if (network.NetworkID == "42767")
                {
                    EXE = @"C:\dev\lc0\19May\lc0\build\lc0_42767.exe";
                }
                else
                {
                    throw new Exception("Unknown net for EXE " + network.NetworkID);
                }

                if (evaluatorDef.Nets[0].Net.Precision == NNEvaluatorPrecision.Int8)
                {
                    lzOptions = lzOptions.Replace("cudnn-fp16", "trt-int8");
                }
                else if (evaluatorDef.Nets[0].Net.Precision == NNEvaluatorPrecision.FP16)
                {
                    lzOptions = lzOptions.Replace("cudnn-fp16", "trt-fp16");
                }
                else
                {
                    throw new NotImplementedException();
                }
            }
#endif

            if (overrideEXE != null)
            {
                EXE = overrideEXE;
            }

            return(EXE, lzOptions);
        }