diff --git a/engine/src/agents/agent.cpp b/engine/src/agents/agent.cpp index 51e5c17a..ccc8de69 100644 --- a/engine/src/agents/agent.cpp +++ b/engine/src/agents/agent.cpp @@ -57,7 +57,7 @@ void Agent::set_must_wait(bool value) mustWait = value; } -Agent::Agent(vector>& nets, PlaySettings* playSettings, bool verbose): +Agent::Agent(const vector>& nets, const PlaySettings* playSettings, bool verbose): NeuralNetAPIUser(nets), playSettings(playSettings), mustWait(true), verbose(verbose), isRunning(false) { diff --git a/engine/src/agents/agent.h b/engine/src/agents/agent.h index 8b2cdea0..1497ef30 100644 --- a/engine/src/agents/agent.h +++ b/engine/src/agents/agent.h @@ -56,7 +56,7 @@ class Agent : public NeuralNetAPIUser protected: SearchLimits* searchLimits; - PlaySettings* playSettings; + const PlaySettings* playSettings; StateObj* state; EvalInfo* evalInfo; // Protect the isRunning attribute and makes sure that the stop() command can only be called after the search has actually been started. @@ -72,7 +72,7 @@ class Agent : public NeuralNetAPIUser bool isRunning; public: - Agent(vector>& nets, PlaySettings* playSettings, bool verbose); + Agent(const vector>& nets, const PlaySettings* playSettings, bool verbose); /** * @brief perform_action Selects an action based on the evaluation result diff --git a/engine/src/agents/mctsagent.cpp b/engine/src/agents/mctsagent.cpp index bef2d68c..ad651b63 100644 --- a/engine/src/agents/mctsagent.cpp +++ b/engine/src/agents/mctsagent.cpp @@ -34,7 +34,7 @@ #include "../node.h" #include "../util/communication.h" -MCTSAgent::MCTSAgent(vector>& netSingleVector, vector>>& netBatchesVector, +MCTSAgent::MCTSAgent(const vector>& netSingleVector, const vector>>& netBatchesVector, SearchSettings* searchSettings, PlaySettings* playSettings): Agent(netSingleVector, playSettings, true), searchSettings(searchSettings), @@ -51,12 +51,8 @@ MCTSAgent::MCTSAgent(vector>& netSingleVector, vectorthreads; ++i) { - vector> netBatchVector; // stores the ith element of all netBatches in netBatchesVector - for (auto& netBatches : netBatchesVector) { - netBatchVector.push_back(std::move(netBatches[i])); - } - searchThreads.emplace_back(new SearchThread(netBatchVector, searchSettings, &mapWithMutex)); + for (size_t idx = 0; idx < searchSettings->threads; ++idx) { + searchThreads.emplace_back(new SearchThread(netBatchesVector[idx], searchSettings, &mapWithMutex)); } timeManager = make_unique(searchSettings->randomMoveFactor); generator = default_random_engine(r()); diff --git a/engine/src/agents/mctsagent.h b/engine/src/agents/mctsagent.h index 3cf9ff23..2d5dceb8 100644 --- a/engine/src/agents/mctsagent.h +++ b/engine/src/agents/mctsagent.h @@ -82,8 +82,8 @@ class MCTSAgent : public Agent unique_ptr threadManager; bool reachedTablebases; public: - MCTSAgent(vector>& netSingleVector, - vector>>& netBatchesVector, + MCTSAgent(const vector>& netSingleVector, + const vector>>& netBatchesVector, SearchSettings* searchSettings, PlaySettings* playSettings); ~MCTSAgent(); diff --git a/engine/src/agents/rawnetagent.cpp b/engine/src/agents/rawnetagent.cpp index f37ee6f9..67ef472d 100644 --- a/engine/src/agents/rawnetagent.cpp +++ b/engine/src/agents/rawnetagent.cpp @@ -29,7 +29,7 @@ using blaze::HybridVector; -RawNetAgent::RawNetAgent(vector>& nets, PlaySettings* playSettings, bool verbose, SearchSettings* searchSettings): +RawNetAgent::RawNetAgent(const vector>& nets, const PlaySettings* playSettings, bool verbose, const SearchSettings* searchSettings): Agent(nets, playSettings, verbose), searchSettings(searchSettings) { diff --git a/engine/src/agents/rawnetagent.h b/engine/src/agents/rawnetagent.h index 92aee49d..90607f27 100644 --- a/engine/src/agents/rawnetagent.h +++ b/engine/src/agents/rawnetagent.h @@ -42,9 +42,9 @@ using namespace crazyara; class RawNetAgent: public Agent { public: - SearchSettings* searchSettings; + const SearchSettings* searchSettings; - RawNetAgent(vector>& nets, PlaySettings* playSettings, bool verbose, SearchSettings* searchSettings); + RawNetAgent(const vector>& nets, const PlaySettings* playSettings, bool verbose, const SearchSettings* searchSettings); RawNetAgent(const RawNetAgent&) = delete; RawNetAgent& operator=(RawNetAgent const&) = delete; diff --git a/engine/src/nn/neuralnetapi.cpp b/engine/src/nn/neuralnetapi.cpp index a7ebb64b..ca314324 100644 --- a/engine/src/nn/neuralnetapi.cpp +++ b/engine/src/nn/neuralnetapi.cpp @@ -229,9 +229,12 @@ Version read_version_from_string(const string &modelFileName) GamePhase read_game_phase_from_string(const string& modelDir) { // use last char of modelDir and convert to int by subtracting '0' - // TODO throw errors if necessary (if last letter is not a digit) - - int gamePhase = (modelDir[modelDir.length() - 2]) - '0'; + // assume phase 0 if last character is not a digit + char phaseChar = modelDir[modelDir.length() - 2]; + if (!std::isdigit(phaseChar)) { + return GamePhase(0); + } + int gamePhase = phaseChar - '0'; return GamePhase(gamePhase); } diff --git a/engine/src/nn/neuralnetapiuser.cpp b/engine/src/nn/neuralnetapiuser.cpp index b3596d84..a2cd63f1 100644 --- a/engine/src/nn/neuralnetapiuser.cpp +++ b/engine/src/nn/neuralnetapiuser.cpp @@ -31,12 +31,13 @@ #include "common.h" #endif -NeuralNetAPIUser::NeuralNetAPIUser(vector>& netsNew) : +NeuralNetAPIUser::NeuralNetAPIUser(const vector>& netsNew) : auxiliaryOutputs(nullptr) { - nets = std::move(netsNew); + for (size_t idx = 0; idx < netsNew.size(); idx++) { + nets.push_back(netsNew[idx].get()); + } numPhases = nets.size(); - for (unsigned int i = 0; i < numPhases; i++) { GamePhase phaseOfNetI = nets[i]->get_game_phase(); diff --git a/engine/src/nn/neuralnetapiuser.h b/engine/src/nn/neuralnetapiuser.h index 07916ab1..692c2c1e 100644 --- a/engine/src/nn/neuralnetapiuser.h +++ b/engine/src/nn/neuralnetapiuser.h @@ -37,7 +37,7 @@ class NeuralNetAPIUser { protected: - vector> nets; // vector of net objects + vector nets; // vector of net objects unsigned int numPhases; std::map phaseToNetsIndex; // maps a GamePhase to the index of the net that should be used @@ -50,7 +50,7 @@ class NeuralNetAPIUser float* auxiliaryOutputs; public: - NeuralNetAPIUser(vector>& netsNew); + NeuralNetAPIUser(const vector>& netsNew); ~NeuralNetAPIUser(); NeuralNetAPIUser(NeuralNetAPIUser&) = delete; diff --git a/engine/src/searchthread.cpp b/engine/src/searchthread.cpp index a58f64ea..b61ad7b5 100644 --- a/engine/src/searchthread.cpp +++ b/engine/src/searchthread.cpp @@ -41,7 +41,7 @@ size_t SearchThread::get_max_depth() const return depthMax; } -SearchThread::SearchThread(vector>& netBatchVector, const SearchSettings* searchSettings, MapWithMutex* mapWithMutex): +SearchThread::SearchThread(const vector>& netBatchVector, const SearchSettings* searchSettings, MapWithMutex* mapWithMutex): NeuralNetAPIUser(netBatchVector), rootNode(nullptr), rootState(nullptr), newState(nullptr), // will be be set via setter methods newNodes(make_unique>(searchSettings->batchSize)), @@ -68,7 +68,7 @@ void SearchThread::set_root_node(Node *value) visitsPreSearch = rootNode->get_visits(); } -void SearchThread::set_search_limits(SearchLimits *s) +void SearchThread::set_search_limits(const SearchLimits *s) { searchLimits = s; } @@ -116,7 +116,7 @@ Node *SearchThread::get_root_node() const return rootNode; } -SearchLimits *SearchThread::get_search_limits() const +const SearchLimits *SearchThread::get_search_limits() const { return searchLimits; } @@ -398,7 +398,6 @@ void SearchThread::thread_iteration() GamePhase majorityPhase = pr->first; phaseCountMap.clear(); - // query the network that corresponds to the majority phase nets[phaseToNetsIndex.at(majorityPhase)]->predict(inputPlanes, valueOutputs, probOutputs, auxiliaryOutputs); set_nn_results_to_child_nodes(); diff --git a/engine/src/searchthread.h b/engine/src/searchthread.h index 99205942..992321b0 100644 --- a/engine/src/searchthread.h +++ b/engine/src/searchthread.h @@ -75,7 +75,7 @@ class SearchThread : public NeuralNetAPIUser MapWithMutex* mapWithMutex; const SearchSettings* searchSettings; - SearchLimits* searchLimits; + const SearchLimits* searchLimits; size_t tbHits; size_t depthSum; size_t depthMax; @@ -89,7 +89,7 @@ class SearchThread : public NeuralNetAPIUser * @param searchSettings Given settings for this search run * @param MapWithMutex Handle to the hash table */ - SearchThread(vector>& netBatchVector, const SearchSettings* searchSettings, MapWithMutex* mapWithMutex); + SearchThread(const vector>& netBatchVector, const SearchSettings* searchSettings, MapWithMutex* mapWithMutex); /** * @brief create_mini_batch Creates a mini-batch of new unexplored nodes. @@ -123,9 +123,9 @@ class SearchThread : public NeuralNetAPIUser void stop(); // Getter, setter functions - void set_search_limits(SearchLimits *s); + void set_search_limits(const SearchLimits *s); Node* get_root_node() const; - SearchLimits *get_search_limits() const; + const SearchLimits *get_search_limits() const; void set_root_node(Node *value); bool is_running() const; void set_is_running(bool value); diff --git a/engine/src/uci/crazyara.cpp b/engine/src/uci/crazyara.cpp index 70fb9421..7c05a44c 100644 --- a/engine/src/uci/crazyara.cpp +++ b/engine/src/uci/crazyara.cpp @@ -51,7 +51,6 @@ CrazyAra::CrazyAra(): rawAgent(nullptr), mctsAgent(nullptr), #ifdef USE_RL - netSingleContender(nullptr), mctsAgentContender(nullptr), #endif searchSettings(SearchSettings()), @@ -368,9 +367,8 @@ void CrazyAra::arena(istringstream &is) { prepare_search_config_structs(); SelfPlay selfPlay(rawAgent.get(), mctsAgent.get(), &searchLimits, &playSettings, &rlSettings, Options); - netSingleContender = create_new_net_single(Options["Model_Directory_Contender"]); - netBatchesContender = create_new_net_batches(Options["Model_Directory_Contender"]); - mctsAgentContender = create_new_mcts_agent(netSingleContender.get(), netBatchesContender, &searchSettings); + fill_nn_vectors(Options["Model_Directory_Contender"], netSingleContenderVector, netBatchesContenderVector); + mctsAgentContender = create_new_mcts_agent(netSingleContenderVector, netBatchesContenderVector, &searchSettings); size_t numberOfGames; is >> numberOfGames; TournamentResult tournamentResult = selfPlay.go_arena(mctsAgentContender.get(), numberOfGames, variant); @@ -401,12 +399,11 @@ void CrazyAra::multimodel_arena(istringstream &is, const string &modelDirectory1 is >> folder; modelDir1 = "m" + std::to_string(folder) + "/"; } - auto mcts1 = create_new_mcts_agent(netSingle.get(), netBatches, &searchSettings, static_cast(type)); + auto mcts1 = create_new_mcts_agent(netSingleVector, netBatchesVector, &searchSettings, static_cast(type)); if (modelDir1 != "") { - netSingle = create_new_net_single(modelDir1); - netBatches = create_new_net_batches(modelDir1); - mcts1 = create_new_mcts_agent(netSingle.get(), netBatches, &searchSettings, static_cast(type)); + fill_nn_vectors(modelDir1, netSingleVector, netBatchesVector); + mcts1 = create_new_mcts_agent(netSingleVector, netBatchesVector, &searchSettings, static_cast(type)); } is >> type; @@ -416,12 +413,11 @@ void CrazyAra::multimodel_arena(istringstream &is, const string &modelDirectory1 is >> folder; modelDir2 = "m" + std::to_string(folder) + "/"; } - auto mcts2 = create_new_mcts_agent(netSingle.get(), netBatches, &searchSettings, static_cast(type)); + auto mcts2 = create_new_mcts_agent(netSingleVector, netBatchesVector, &searchSettings, static_cast(type)); if (modelDir2 != "") { - netSingleContender = create_new_net_single(modelDir2); - netBatchesContender = create_new_net_batches(modelDir2); - mcts2 = create_new_mcts_agent(netSingleContender.get(), netBatchesContender, &searchSettings, static_cast(type)); + fill_nn_vectors(modelDir2, netSingleContenderVector, netBatchesContenderVector); + mcts2 = create_new_mcts_agent(netSingleContenderVector, netBatchesContenderVector, &searchSettings, static_cast(type)); } SelfPlay selfPlay(rawAgent.get(), mcts1.get(), &searchLimits, &playSettings, &rlSettings, Options); @@ -549,13 +545,55 @@ void CrazyAra::init() #endif } +void CrazyAra::fill_single_nn_vector(const string& modelDirectory, vector>& netSingleVector, vector>>& netBatchesVector) +{ + unique_ptr netSingleTmp = create_new_net(modelDirectory, int(Options["First_Device_ID"]), 1); + netSingleTmp->validate_neural_network(); + netSingleVector.push_back(std::move(netSingleTmp)); + + size_t idx = 0; + for (int deviceId = int(Options["First_Device_ID"]); deviceId <= int(Options["Last_Device_ID"]); ++deviceId) { + for (size_t i = 0; i < size_t(Options["Threads"]); ++i) { + unique_ptr netBatchesTmp = create_new_net(modelDirectory, deviceId, searchSettings.batchSize); + netBatchesTmp->validate_neural_network(); + netBatchesVector[idx].push_back(std::move(netBatchesTmp)); + ++idx; + } + } +} + +void CrazyAra::fill_nn_vectors(const string& modelDirectory, vector>& netSingleVector, vector>>& netBatchesVector) +{ + netSingleVector.clear(); + netBatchesVector.clear(); + // threads is the first dimension, the phase are the 2nd dimension + netBatchesVector.resize(Options["Threads"] * get_num_gpus(Options)); + + // early return if no phases are used + for (const auto& entry : fs::directory_iterator(modelDirectory)) { + if (!fs::is_directory(entry.path())) { + fill_single_nn_vector(modelDirectory, netSingleVector, netBatchesVector); + return; + } + else { + break; + } + } + + // analyse directory to get num phases + for (const auto& entry : fs::directory_iterator(modelDirectory)) { + std::cout << entry.path().generic_string() << std::endl; + + fill_single_nn_vector(entry.path().generic_string(), netSingleVector, netBatchesVector); + } +} + + template bool CrazyAra::is_ready() { bool hasReplied = false; if (!networkLoaded) { - netSingleVector.clear(); - netBatchesVector.clear(); const size_t timeoutMS = Options["Timeout_MS"]; TimeOutReadyThread timeoutThread(timeoutMS); thread tTimeoutThread; @@ -568,22 +606,10 @@ bool CrazyAra::is_ready() init_rl_settings(); #endif - // analyse directory to get num phases - for (const auto& entry : fs::directory_iterator(string(Options["Model_Directory"]))) { - std::cout << entry.path().generic_string() << std::endl; - - unique_ptr netSingleTmp = create_new_net_single(entry.path().generic_string()); - netSingleTmp->validate_neural_network(); - vector> netBatchesTmp = create_new_net_batches(entry.path().generic_string()); - netBatchesTmp.front()->validate_neural_network(); - - netSingleVector.push_back(std::move(netSingleTmp)); - netBatchesVector.push_back(std::move(netBatchesTmp)); - } + fill_nn_vectors(Options["Model_Directory"], netSingleVector, netBatchesVector); mctsAgent = create_new_mcts_agent(netSingleVector, netBatchesVector, &searchSettings); - //rawAgent = make_unique(netSingleVector, &playSettings, false, &searchSettings); - // TODO: rawAgent currently doesn't work (netSingleVector somehow doesn't contain any nets) + rawAgent = make_unique(netSingleVector, &playSettings, false, &searchSettings); StateConstants::init(mctsAgent->is_policy_map(), Options["UCI_Chess960"]); timeoutThread.kill(); @@ -617,40 +643,21 @@ string CrazyAra::engine_info() return ss.str(); } -unique_ptr CrazyAra::create_new_net_single(const string& modelDirectory) +unique_ptr CrazyAra::create_new_net(const string& modelDirectory, int deviceId, unsigned int batchSize) { -#ifdef MXNET - return make_unique(Options["Context"], int(Options["First_Device_ID"]), 1, modelDirectory, Options["Precision"], false); -#elif defined TENSORRT - return make_unique(int(Options["First_Device_ID"]), 1, modelDirectory, Options["Precision"]); -#elif defined OPENVINO - return make_unique(int(Options["First_Device_ID"]), 1, modelDirectory, Options["Threads_NN_Inference"]); -#endif - return nullptr; -} - -vector> CrazyAra::create_new_net_batches(const string& modelDirectory) -{ - vector> netBatches; #ifdef MXNET #ifdef TENSORRT const bool useTensorRT = bool(Options["Use_TensorRT"]); #else const bool useTensorRT = false; #endif + return make_unique(Options["Context"], deviceId, batchSize, modelDirectory, Options["Precision"], useTensorRT); +#elif defined TENSORRT + return make_unique(deviceId, batchSize, modelDirectory, Options["Precision"]); +#elif defined OPENVINO + return make_unique(deviceId, batchSize, modelDirectory, Options["Threads_NN_Inference"]); #endif - for (int deviceId = int(Options["First_Device_ID"]); deviceId <= int(Options["Last_Device_ID"]); ++deviceId) { - for (size_t i = 0; i < size_t(Options["Threads"]); ++i) { - #ifdef MXNET - netBatches.push_back(make_unique(Options["Context"], deviceId, searchSettings.batchSize, modelDirectory, Options["Precision"], useTensorRT)); - #elif defined TENSORRT - netBatches.push_back(make_unique(deviceId, searchSettings.batchSize, modelDirectory, Options["Precision"])); - #elif defined OPENVINO - netBatches.push_back(make_unique(deviceId, searchSettings.batchSize, modelDirectory, Options["Threads_NN_Inference"])); - #endif - } - } - return netBatches; + return nullptr; } void CrazyAra::set_uci_option(istringstream &is, StateObj& state) diff --git a/engine/src/uci/crazyara.h b/engine/src/uci/crazyara.h index 55f4d4ba..cb56157f 100644 --- a/engine/src/uci/crazyara.h +++ b/engine/src/uci/crazyara.h @@ -85,9 +85,9 @@ class CrazyAra vector> netSingleVector; vector>> netBatchesVector; #ifdef USE_RL - unique_ptr netSingleContender; + vector> netSingleContenderVector; unique_ptr mctsAgentContender; - vector> netBatchesContender; + vector>> netBatchesContenderVector; RLSettings rlSettings; #endif SearchSettings searchSettings; @@ -280,18 +280,29 @@ class CrazyAra unique_ptr create_new_mcts_agent(vector>& netSingleVector, vector>>& netBatchesVector, SearchSettings* searchSettings, MCTSAgentType type = MCTSAgentType::kDefault); /** - * @brief create_new_net_single Factory to create and load a new model from a given directory - * @param modelDirectory Model directory where the .params and .json files are stored + * @brief create_new_net Factory to create and load a new model from a given directory + * @param modelDirectory Model directory where the .onnx file is stored. + * @param deviceId Device index that will be used for inference. + * @param batchSize Mini batch size used for inference. * @return Pointer to the newly created object */ - unique_ptr create_new_net_single(const string& modelDirectory); + unique_ptr create_new_net(const string& modelDirectory, int deviceId, unsigned int batchSize); /** - * @brief create_new_net_batches Factory to create and load a new model for batch-size access - * @param modelDirectory Model directory where the .params and .json files are stored - * @return Vector of pointers to the newly createded objects. For every thread a sepreate net. + * @brief fill_single_nn_vector Fills a single phase in netSingleVector and netBatchesVector with a loaded neural network. + * @param modelDirectory Model directory where the .onnx file is stored. + * @param netSingleVector Vector of neural networks with batch-size 1 + * @param netBatchesVector Vector of neural networks with batch-size > 1 */ - vector> create_new_net_batches(const string& modelDirectory); + void fill_single_nn_vector(const string& modelDirectory, vector>& netSingleVector, vector>>& netBatchesVector); + + /** + * @brief fill_nn_vectors Fills the given neural network vectors with loaded neural network models. + * @param modelDirectory Model directory where the .onnx file is stored. + * @param netSingleVector Vector of neural networks with batch-size 1 + * @param netBatchesVector Vector of neural networks with batch-size > 1 + */ + void fill_nn_vectors(const string& modelDirectory, vector>& netSingleVector, vector>>& netBatchesVector); /** * @brief set_uci_option Updates an UCI option using the given input stream and set changedUCIoption to true. diff --git a/engine/src/uci/optionsuci.cpp b/engine/src/uci/optionsuci.cpp index 30b9e104..dce9b3e5 100644 --- a/engine/src/uci/optionsuci.cpp +++ b/engine/src/uci/optionsuci.cpp @@ -138,8 +138,8 @@ void OptionsUCI::init(OptionsMap &o) o["Nodes"] << Option(800, 0, 99999999); #else o["Nodes"] << Option(0, 0, 99999999); - o["Nodes_Limit"] << Option(0, 0, 999999999); #endif + o["Nodes_Limit"] << Option(0, 0, 999999999); #ifdef TENSORRT o["Precision"] << Option("float16", {"float32", "float16", "int8"}); #else