diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..c78723a7a --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,197 @@ +name: Build +on: + push: + paths: + - 'cpp/**' + - '.github/workflows/build.yml' + +jobs: + xcodebuild: + runs-on: macos-13 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run Xcode build + run: | + cd cpp/xcode + /Applications/Xcode_15.0.1.app/Contents/Developer/usr/bin/xcodebuild -derivedDataPath DerivedData -scheme katago -configuration Debug build + + - name: Setup configuration + run: | + ln -s ../../../../../configs/misc/coreml_example.cfg cpp/xcode/DerivedData/Build/Products/Debug/gtp.cfg + ln -s ../../../../../configs/misc/metal_gtp.cfg cpp/xcode/DerivedData/Build/Products/Debug/metal_gtp.cfg + + - name: Setup network + run: | + mkdir -p models + cd models + wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/kata1-b18c384nbt-s9996604416-d4316597426.bin.gz + ln -s ../../../../../../models/kata1-b18c384nbt-s9996604416-d4316597426.bin.gz ../cpp/xcode/DerivedData/Build/Products/Debug/model.bin.gz + + - name: Setup network of version 8 + run: | + mkdir -p models + cd models + wget https://github.com/lightvector/KataGo/releases/download/v1.4.5/g170-b40c256x2-s5095420928-d1229425124.bin.gz + ln -s ../../../../../../models/g170-b40c256x2-s5095420928-d1229425124.bin.gz ../cpp/xcode/DerivedData/Build/Products/Debug/modelv8.bin.gz + + - name: Setup CoreML model FP16 + run: | + mkdir -p models + cd models + wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/KataGoModel19x19fp16v14s9996604416.mlpackage.zip + unzip KataGoModel19x19fp16v14s9996604416.mlpackage.zip + ln -s ../../../../../../models/KataGoModel19x19fp16v14s9996604416.mlpackage ../cpp/xcode/DerivedData/Build/Products/Debug/KataGoModel19x19fp16.mlpackage + + - name: Setup CoreML model FP32 + run: | + mkdir -p models + cd models + wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/KataGoModel19x19fp32v14s9996604416.mlpackage.zip + unzip KataGoModel19x19fp32v14s9996604416.mlpackage.zip + ln -s ../../../../../../models/KataGoModel19x19fp32v14s9996604416.mlpackage ../cpp/xcode/DerivedData/Build/Products/Debug/KataGoModel19x19fp32.mlpackage + + - name: Setup CoreML model FP32 meta + run: | + mkdir -p models + cd models + wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/KataGoModel19x19fp32v15m1humanv0.mlpackage.zip + unzip KataGoModel19x19fp32v15m1humanv0.mlpackage.zip + ln -s ../../../../../../models/KataGoModel19x19fp32v15m1humanv0.mlpackage ../cpp/xcode/DerivedData/Build/Products/Debug/KataGoModel19x19fp32m1.mlpackage + + - name: Setup test data + run: | + ln -s ../../../../../tests cpp/xcode/DerivedData/Build/Products/Debug/tests + + - name: Run Xcode test + run: | + cd cpp/xcode + /Applications/Xcode_15.0.1.app/Contents/Developer/usr/bin/xcodebuild -derivedDataPath DerivedData -scheme katago -configuration Debug test + + - name: Run KataGo tests + run: | + cd cpp/xcode/DerivedData/Build/Products/Debug + # ./katago runnnlayertests + ./katago runoutputtests + ./katago runnnontinyboardtest model.bin.gz false false 0 false + ./katago runnnsymmetriestest model.bin.gz false false false + ./katago runownershiptests gtp.cfg model.bin.gz + + cmake-macos: + runs-on: macos-13 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup ninja + run: | + brew install ninja + + - name: Setup Eigen + run: | + brew install eigen + + - name: Setup Xcode + run: | + xcode-select -p + sudo xcode-select -s /Applications/Xcode_15.0.1.app/Contents/Developer + + - name: Build KataGo with Eigen backend + run: | + mkdir -p cpp/build + cd cpp/build + cmake -G Ninja -DUSE_BACKEND=EIGEN ../ + ninja + + - name: Setup network + run: | + mkdir -p models + cd models + wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/kata1-b18c384nbt-s9996604416-d4316597426.bin.gz + ln -s ../../models/kata1-b18c384nbt-s9996604416-d4316597426.bin.gz ../cpp/build/model.bin.gz + + - name: Run KataGo GPU error test with Eigen backend + run: | + cd cpp/build + ./katago testgpuerror -config ../configs/gtp_example.cfg -model model.bin.gz -boardsize 9 -reference-file base.bin + + - name: Setup human SL network + run: | + mkdir -p models + cd models + wget https://github.com/lightvector/KataGo/releases/download/v1.15.0/b18c384nbt-humanv0.bin.gz + ln -s ../../models/b18c384nbt-humanv0.bin.gz ../cpp/build/b18c384nbt-humanv0.bin.gz + + - name: Run KataGo GPU error test of human SL network with Eigen backend + run: | + cd cpp/build + ./katago testgpuerror -config ../configs/misc/gtp_human5k_coreml.cfg -model b18c384nbt-humanv0.bin.gz -boardsize 9 -reference-file base-humanv0.bin + + - name: Build KataGo with CoreML backend + run: | + cd cpp + mv CMakeLists.txt-macos CMakeLists.txt + mkdir -p build + cd build + cmake -G Ninja ../ + ninja + + - name: Setup configuration + run: | + ln -s ../configs/misc/coreml_example.cfg cpp/build/gtp.cfg + + - name: Setup CoreML model FP16 + run: | + mkdir -p models + cd models + wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/KataGoModel19x19fp16v14s9996604416.mlpackage.zip + unzip KataGoModel19x19fp16v14s9996604416.mlpackage.zip + ln -s ../../models/KataGoModel19x19fp16v14s9996604416.mlpackage ../cpp/build/KataGoModel19x19fp16.mlpackage + + - name: Setup CoreML model FP32 + run: | + mkdir -p models + cd models + wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/KataGoModel19x19fp32v14s9996604416.mlpackage.zip + unzip KataGoModel19x19fp32v14s9996604416.mlpackage.zip + ln -s ../../models/KataGoModel19x19fp32v14s9996604416.mlpackage ../cpp/build/KataGoModel19x19fp32.mlpackage + + - name: Run KataGo GPU error test with CoreML backend + run: | + cd cpp/build + ./katago testgpuerror -config gtp.cfg -model model.bin.gz -boardsize 9 -reference-file base.bin + + - name: Setup CoreML model FP16 of human SL network + run: | + mkdir -p models + cd models + wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/KataGoModel19x19fp16v15m1humanv0.mlpackage.zip + unzip KataGoModel19x19fp16v15m1humanv0.mlpackage.zip + ln -s ../../models/KataGoModel19x19fp16v15m1humanv0.mlpackage ../cpp/build/KataGoModel19x19fp16m1.mlpackage + + - name: Setup CoreML model FP32 of human SL network + run: | + mkdir -p models + cd models + wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/KataGoModel19x19fp32v15m1humanv0.mlpackage.zip + unzip KataGoModel19x19fp32v15m1humanv0.mlpackage.zip + ln -s ../../models/KataGoModel19x19fp32v15m1humanv0.mlpackage ../cpp/build/KataGoModel19x19fp32m1.mlpackage + + - name: Run KataGo GPU error test of human SL network with CoreML backend + run: | + cd cpp/build + ./katago testgpuerror -config ../configs/misc/gtp_human5k_coreml.cfg -model b18c384nbt-humanv0.bin.gz -boardsize 9 -reference-file base-humanv0.bin + + - name: Setup test data + run: | + ln -s ../tests cpp/build/tests + + - name: Run KataGo tests + run: | + cd cpp/build + # ./katago runnnlayertests + ./katago runoutputtests + ./katago runnnontinyboardtest model.bin.gz false false 0 false + ./katago runnnsymmetriestest model.bin.gz false false false + ./katago runownershiptests gtp.cfg model.bin.gz diff --git a/.gitignore b/.gitignore index 2e933d553..a66c6a678 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,16 @@ GPATH GRTAGS GTAGS +# For Xcode +xcuserdata/ +DerivedData/ +*.plist + +# For KataGo-iOS +ios/KataGo\ iOS/Resources/*.bin.gz +ios/KataGo\ iOS/Resources/*.mlpackage + +# misc cpp/external/httplib/cpp-httplib/ cpp/external/nlohmann_json/nlohmann_json gtp.cfg diff --git a/cpp/CMakeLists.txt-macos b/cpp/CMakeLists.txt-macos new file mode 100644 index 000000000..09657427d --- /dev/null +++ b/cpp/CMakeLists.txt-macos @@ -0,0 +1,297 @@ +cmake_minimum_required(VERSION 3.26) + +if(NOT "${CMAKE_GENERATOR}" STREQUAL "Ninja") + message(FATAL_ERROR "Bidirectional C++ Interop requires Ninja generator. Have ${CMAKE_GENERATOR}") +endif() + +project(katago LANGUAGES CXX Swift) + +if("${CMAKE_Swift_COMPILER_VERSION}" VERSION_LESS 5.9) + message(FATAL_ERROR "Bidirectional C++ Interop requires Swift 5.9 or greater. Have ${CMAKE_Swift_COMPILER_VERSION}") +endif() + +if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + message(FATAL_ERROR "Project requires building with AppleClang. Have ${CMAKE_CXX_COMPILER_ID}") +endif() + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/macos/cmake/modules") +include(InitializeSwift) +include(AddSwift) + +set(CMAKE_OSX_DEPLOYMENT_TARGET 13.0) +set(CMAKE_CXX_STANDARD 14) + +include_directories(external) +include_directories(external/tclap-1.2.2/include) +include_directories(SYSTEM external/filesystem-1.5.8/include) #SYSTEM suppresses a few warnings + +#--------------------------- PLATFORM SPECIFIC ------------------------------------------------------------------------- + +if(NOT WIN32) + string(ASCII 27 Esc) + set(ColorReset "${Esc}[m") + set(ColorBold "${Esc}[1m") + set(ColorRed "${Esc}[31m") + set(ColorBoldRed "${ColorRed}${ColorBold}") +endif() + +#--------------------------- CMAKE VARIABLES (partly for Cmake GUI) ---------------------------------------------------- + +set(BUILD_DISTRIBUTED 0 CACHE BOOL "Build with http support for contributing to distributed training") +set(NO_GIT_REVISION 0 CACHE BOOL "Disable embedding the git revision into the compiled exe") +set(USE_BIGGER_BOARDS_EXPENSIVE 0 CACHE BOOL "Allow boards up to size 29. Compiling with this will use more memory and slow down KataGo, even when playing on boards of size 19.") + +#--------------------------- NEURAL NET BACKEND ------------------------------------------------------------------------ + +message(STATUS "Building 'katago' executable for GTP engine and other tools.") +message(STATUS "Using CoreML backend.") +set(NEURALNET_BACKEND_SOURCES + ../neuralnet/coremlbackend.cpp + ../neuralnet/metalbackend.cpp + ) + +#--------------------------- GIT --------------------------------------------------------------------------------------- + +if(NO_GIT_REVISION AND (NOT BUILD_DISTRIBUTED)) + message(STATUS "-DNO_GIT_REVISION=1 is set, avoiding including the Git revision in compiled executable") + unset(GIT_HEADER_FILE_ALWAYS_UPDATED) +else() + if(NO_GIT_REVISION AND BUILD_DISTRIBUTED) + message(STATUS "${ColorRed}NO_GIT_REVISION is set, but BUILD_DISTRIBUTED is also set and distributed requires git revision, so ignoring NO_GIT_REVISION.${ColorReset}") + elseif(BUILD_DISTRIBUTED) + message(STATUS "Including Git revision in the compiled executable") + else() + message(STATUS "Including Git revision in the compiled executable, specify -DNO_GIT_REVISION=1 to disable") + endif() + find_package(Git) + if(NOT GIT_FOUND) + set(GIT_EXECUTABLE ${GIT_EXECUTABLE} CACHE FILEPATH "Path to git executable") + mark_as_advanced(CLEAR GIT_EXECUTABLE) + if(BUILD_DISTRIBUTED) + message(SEND_ERROR "${ColorBoldRed}Git executable was not found, specify GIT_EXECUTABLE as the path to the git executable.${ColorReset}") + else() + message(SEND_ERROR "${ColorBoldRed}Git executable was not found. Either specify GIT_EXECUTABLE as the path to the git executable, or use NO_GIT_REVISION to disable.${ColorReset}") + endif() + endif() + set(GIT_HEADER_FILE_TEMPLATE_BARE program/gitinfotemplate.h) + set(GIT_HEADER_FILE_ALWAYS_UPDATED_BARE program/gitinfoupdated.h) + set(GIT_HEADER_FILE_BARE program/gitinfo.h) + set(GIT_HEADER_FILE_TEMPLATE ${CMAKE_SOURCE_DIR}/${GIT_HEADER_FILE_TEMPLATE_BARE}) + set(GIT_HEADER_FILE_ALWAYS_UPDATED ${CMAKE_BINARY_DIR}/${GIT_HEADER_FILE_ALWAYS_UPDATED_BARE}) + set(GIT_HEADER_FILE ${CMAKE_BINARY_DIR}/${GIT_HEADER_FILE_BARE}) + add_custom_command( + OUTPUT ${GIT_HEADER_FILE_ALWAYS_UPDATED} + COMMAND ${CMAKE_COMMAND} -E copy ${GIT_HEADER_FILE_TEMPLATE} ${GIT_HEADER_FILE_ALWAYS_UPDATED} + COMMAND ${GIT_EXECUTABLE} describe --match=DummyTagNotExisting --always --abbrev=40 --dirty >> ${GIT_HEADER_FILE_ALWAYS_UPDATED} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${GIT_HEADER_FILE_ALWAYS_UPDATED} ${GIT_HEADER_FILE} + COMMAND ${CMAKE_COMMAND} -E remove ${GIT_HEADER_FILE_ALWAYS_UPDATED} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + VERBATIM + ) +endif() + +#--------------------------- C++ Swift Interop -------------------------------- + +_swift_generate_cxx_header_target( + KataGoSwift_Swift_h + KataGoSwift + "${CMAKE_CURRENT_BINARY_DIR}/include/KataGoSwift/KataGoSwift-swift.h" + SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/neuralnet/coremlbackend.swift" + "${CMAKE_CURRENT_SOURCE_DIR}/neuralnet/coremlmodel.swift" + "${CMAKE_CURRENT_SOURCE_DIR}/neuralnet/metalbackend.swift") + +add_library(KataGoSwift STATIC + neuralnet/coremlbackend.swift + neuralnet/coremlmodel.swift + neuralnet/metalbackend.swift) + +add_dependencies(KataGoSwift KataGoSwift_Swift_h) +target_include_directories(KataGoSwift PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/include") +set_target_properties(KataGoSwift PROPERTIES Swift_MODULE_NAME "KataGoSwift") +target_compile_options(KataGoSwift PUBLIC + "$<$:-cxx-interoperability-mode=default>") + +#--------------------------- KATAGO COMPILING AND LINKING -------------------------------------------------------------- + +add_executable(katago + ../core/global.cpp + ../core/base64.cpp + ../core/bsearch.cpp + ../core/commandloop.cpp + ../core/config_parser.cpp + ../core/datetime.cpp + ../core/elo.cpp + ../core/fancymath.cpp + ../core/fileutils.cpp + ../core/hash.cpp + ../core/logger.cpp + ../core/mainargs.cpp + ../core/makedir.cpp + ../core/md5.cpp + ../core/multithread.cpp + ../core/parallel.cpp + ../core/rand.cpp + ../core/rand_helpers.cpp + ../core/sha2.cpp + ../core/test.cpp + ../core/threadsafecounter.cpp + ../core/threadsafequeue.cpp + ../core/threadtest.cpp + ../core/timer.cpp + ../game/board.cpp + ../game/rules.cpp + ../game/boardhistory.cpp + ../game/graphhash.cpp + ../dataio/sgf.cpp + ../dataio/numpywrite.cpp + ../dataio/poswriter.cpp + ../dataio/trainingwrite.cpp + ../dataio/loadmodel.cpp + ../dataio/homedata.cpp + ../dataio/files.cpp + ../neuralnet/nninputs.cpp + ../neuralnet/sgfmetadata.cpp + ../neuralnet/modelversion.cpp + ../neuralnet/nneval.cpp + ../neuralnet/desc.cpp + ${NEURALNET_BACKEND_SOURCES} + ../book/book.cpp + ../book/bookcssjs.cpp + ../search/timecontrols.cpp + ../search/searchparams.cpp + ../search/mutexpool.cpp + ../search/search.cpp + ../search/searchnode.cpp + ../search/searchresults.cpp + ../search/searchhelpers.cpp + ../search/searchexplorehelpers.cpp + ../search/searchmirror.cpp + ../search/searchmultithreadhelpers.cpp + ../search/searchnnhelpers.cpp + ../search/searchtimehelpers.cpp + ../search/searchupdatehelpers.cpp + ../search/asyncbot.cpp + ../search/distributiontable.cpp + ../search/localpattern.cpp + ../search/searchnodetable.cpp + ../search/subtreevaluebiastable.cpp + ../search/patternbonustable.cpp + ../search/analysisdata.cpp + ../search/reportedsearchvalues.cpp + ../program/gtpconfig.cpp + ../program/setup.cpp + ../program/playutils.cpp + ../program/playsettings.cpp + ../program/play.cpp + ../program/selfplaymanager.cpp + ${GIT_HEADER_FILE_ALWAYS_UPDATED} + ../tests/testboardarea.cpp + ../tests/testboardbasic.cpp + ../tests/testbook.cpp + ../tests/testcommon.cpp + ../tests/testconfig.cpp + ../tests/testmisc.cpp + ../tests/testnnevalcanary.cpp + ../tests/testrules.cpp + ../tests/testscore.cpp + ../tests/testsgf.cpp + ../tests/testsymmetries.cpp + ../tests/testnninputs.cpp + ../tests/testownership.cpp + ../tests/testsearchcommon.cpp + ../tests/testsearchnonn.cpp + ../tests/testsearch.cpp + ../tests/testsearchv3.cpp + ../tests/testsearchv8.cpp + ../tests/testsearchv9.cpp + ../tests/testsearchmisc.cpp + ../tests/testtime.cpp + ../tests/testtrainingwrite.cpp + ../tests/testnn.cpp + ../tests/tinymodel.cpp + ../tests/tinymodeldata.cpp + ../distributed/client.cpp + ../command/commandline.cpp + ../command/analysis.cpp + ../command/benchmark.cpp + ../command/contribute.cpp + ../command/evalsgf.cpp + ../command/gatekeeper.cpp + ../command/genbook.cpp + ../command/gputest.cpp + ../command/gtp.cpp + ../command/match.cpp + ../command/misc.cpp + ../command/runtests.cpp + ../command/sandbox.cpp + ../command/selfplay.cpp + ../command/tune.cpp + ../command/writetrainingdata.cpp + ../main.cpp + ) + +target_compile_definitions(katago PRIVATE DEBUG) +target_compile_definitions(katago PRIVATE USE_COREML_BACKEND) + +if(USE_BIGGER_BOARDS_EXPENSIVE) + target_compile_definitions(katago PRIVATE COMPILE_MAX_BOARD_LEN=29) +endif() + +if(NO_GIT_REVISION AND (NOT BUILD_DISTRIBUTED)) + target_compile_definitions(katago PRIVATE NO_GIT_REVISION) +endif() + +find_package(ZLIB) +if(ZLIB_FOUND) + include_directories(${ZLIB_INCLUDE_DIRS}) + target_link_libraries(katago ${ZLIB_LIBRARIES}) +else() + set(ZLIB_INCLUDE_DIR ${ZLIB_INCLUDE_DIR} CACHE PATH "Path to directory with zlib.h and other header files") + set(ZLIB_LIBRARY ${ZLIB_LIBRARY} CACHE FILEPATH "Path to 'libz.so' on Linux or 'libz.lib' on Windows") + mark_as_advanced(CLEAR ZLIB_INCLUDE_DIR ZLIB_LIBRARY) + message(SEND_ERROR "${ColorBoldRed}zlib was not found, if zlib is actually installed but not being found you can set ZLIB_INCLUDE_DIR to the directory with zlib.h and other headers, and ZLIB_LIBRARY to the compiled library 'libz.so' on Linux or 'libz.lib' on Windows. On the command line, this is -DZLIB_INCLUDE_DIR=... and -DZLIB_LIBRARY=... ${ColorReset}") +endif(ZLIB_FOUND) + +find_library(LIBZIP_LIBRARY NAMES zip) +find_path(LIBZIP_INCLUDE_DIR_ZIP NAMES zip.h) +find_path(LIBZIP_INCLUDE_DIR_ZIPCONF NAMES zipconf.h) +if((NOT LIBZIP_LIBRARY) OR (NOT LIBZIP_INCLUDE_DIR_ZIP) OR (NOT LIBZIP_INCLUDE_DIR_ZIPCONF)) + if(BUILD_DISTRIBUTED) + message(SEND_ERROR "${ColorBoldRed}WARNING: BUILD_DISTRIBUTED was requested but libzip library was NOT found. KataGo needs this for writing training data so libzip is required. On Linux, install through your normal package manager. On Windows, set LIBZIP_INCLUDE_DIR_ZIP to the directory that includes zip.h and other files, and LIBZIP_INCLUDE_DIR_ZIPCONF to the directory that includes zipconf.h and other files, and LIBZIP_LIBRARY to the libzip.lib or zip.lib file. ${ColorReset}") + endif() + target_compile_definitions(katago PRIVATE NO_LIBZIP) + message(WARNING "${ColorBoldRed}WARNING: libzip library was NOT found. KataGo should still work for GTP/matches/analysis if everything else is good, but selfplay for writing training data will not be possible.${ColorReset}") + set(LIBZIP_INCLUDE_DIR_ZIP ${LIBZIP_INCLUDE_DIR_ZIP} CACHE PATH "Path to directory with zip.h and other header files") + set(LIBZIP_INCLUDE_DIR_ZIPCONF ${LIBZIP_INCLUDE_DIR_ZIPCONF} CACHE PATH "Path to directory with zipconf.h and other header files") + set(LIBZIP_LIBRARY ${LIBZIP_LIBRARY} CACHE FILEPATH "Path to 'libzip.so' on Linux or 'libzip.lib' or 'zip.lib' on Windows") + mark_as_advanced(CLEAR LIBZIP_INCLUDE_DIR_ZIP LIBZIP_INCLUDE_DIR_ZIPCONF LIBZIP_LIBRARY) +else() + include_directories(${LIBZIP_INCLUDE_DIR_ZIP}) + include_directories(${LIBZIP_INCLUDE_DIR_ZIPCONF}) + target_link_libraries(katago ${LIBZIP_LIBRARY}) +endif() + +if(BUILD_DISTRIBUTED) + message(STATUS "-DBUILD_DISTRIBUTED=1 is set, compiling code and dependencies to contribute to distributed training") + target_compile_definitions(katago PRIVATE BUILD_DISTRIBUTED) + find_package(OpenSSL REQUIRED) + target_link_libraries(katago ${OPENSSL_SSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES}) + include_directories(${OPENSSL_INCLUDE_DIR}) + include_directories(external/httplib) +endif() + +#------------------------------------------------------------------------------------ + +message(STATUS "Setting up build for AppleClang.") +target_link_libraries(katago KataGoSwift) +find_package (Threads REQUIRED) +target_link_libraries(katago Threads::Threads) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 -Wall -Wextra -Wno-sign-compare -Wcast-align -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Wmissing-declarations -Wmissing-include-dirs -Woverloaded-virtual -Wredundant-decls -Wshadow -Wstrict-overflow=1 -Wswitch-default -Wfloat-conversion -Wunused -Wno-c++17-extensions") +message(STATUS "Enabling AppleClang-specific build options.") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnull-dereference -Wdangling-else") + +target_include_directories(katago PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + +if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") + message(WARNING "You are currently running cmake on an Intel-based processor. It is known that running KataGo in this configuration may encounter performance issues. It is recommended to switch to a cmake version designed for ARM64 architecture for optimal performance.") +endif() diff --git a/cpp/command/benchmark.cpp b/cpp/command/benchmark.cpp index 949a436fc..63db5c0e3 100644 --- a/cpp/command/benchmark.cpp +++ b/cpp/command/benchmark.cpp @@ -267,6 +267,9 @@ int MainCmds::benchmark(const vector& args) { #endif #ifdef USE_EIGEN_BACKEND cout << "You are currently using the Eigen (CPU) version of KataGo. Due to having no GPU, it may be slow." << endl; +#endif +#ifdef USE_COREML_BACKEND + cout << "You are currently using the CoreML version of KataGo." << endl; #endif cout << endl; cout << "Your GTP config is currently set to use numSearchThreads = " << params.numThreads << endl; diff --git a/cpp/command/commandline.cpp b/cpp/command/commandline.cpp index 857e9672d..a3741bc3f 100644 --- a/cpp/command/commandline.cpp +++ b/cpp/command/commandline.cpp @@ -165,7 +165,9 @@ class KataHelpOutput : public TCLAP::StdOutput KataGoCommandLine::KataGoCommandLine(const string& message) :TCLAP::CmdLine(message, ' ', Version::getKataGoVersionFullInfo(),true), modelFileArg(NULL), + coreMLModelFileArg(NULL), humanModelFileArg(NULL), + humanCoreMLModelFileArg(NULL), configFileArg(NULL), overrideConfigArg(NULL), defaultConfigFileName(), @@ -178,7 +180,9 @@ KataGoCommandLine::KataGoCommandLine(const string& message) KataGoCommandLine::~KataGoCommandLine() { delete modelFileArg; + delete coreMLModelFileArg; delete humanModelFileArg; + delete humanCoreMLModelFileArg; delete configFileArg; delete overrideConfigArg; delete helpOutput; @@ -211,6 +215,15 @@ void KataGoCommandLine::addModelFileArg() { this->add(*modelFileArg); } +void KataGoCommandLine::addCoreMLModelFileArg() { + assert(coreMLModelFileArg == NULL); + string helpDesc = "Core ML model file"; + bool required = false; + string defaultPath = ""; + coreMLModelFileArg = new TCLAP::ValueArg("","coreml-model",helpDesc,required,defaultPath,"FILE"); + this->add(*coreMLModelFileArg); +} + void KataGoCommandLine::addHumanModelFileArg() { assert(humanModelFileArg == NULL); string helpDesc = "Human SL neural net model file"; @@ -220,6 +233,15 @@ void KataGoCommandLine::addHumanModelFileArg() { this->add(*humanModelFileArg); } +void KataGoCommandLine::addHumanCoreMLModelFileArg() { + assert(humanCoreMLModelFileArg == NULL); + string helpDesc = "Human SL Core ML model file"; + bool required = false; + string defaultPath = ""; + humanCoreMLModelFileArg = new TCLAP::ValueArg("","human-coreml-model",helpDesc,required,defaultPath,"FILE"); + this->add(*humanCoreMLModelFileArg); +} + //Empty string indicates no default void KataGoCommandLine::addConfigFileArg(const string& defaultCfgFileName, const string& exampleConfigFile) { bool required = true; @@ -278,6 +300,11 @@ string KataGoCommandLine::getModelFile() const { return modelFile; } +string KataGoCommandLine::getCoreMLModelFile() const { + assert(coreMLModelFileArg != NULL); + return coreMLModelFileArg->getValue(); +} + bool KataGoCommandLine::modelFileIsDefault() const { return modelFileArg->getValue().empty(); } @@ -288,6 +315,11 @@ string KataGoCommandLine::getHumanModelFile() const { return humanModelFileArg->getValue(); } +string KataGoCommandLine::getHumanCoreMLModelFile() const { + assert(humanCoreMLModelFileArg != NULL); + return humanCoreMLModelFileArg->getValue(); +} + vector KataGoCommandLine::getConfigFiles() const { assert(configFileArg != NULL); vector configFiles = configFileArg->getValue(); diff --git a/cpp/command/commandline.h b/cpp/command/commandline.h index f94d603b2..a7819f2f4 100644 --- a/cpp/command/commandline.h +++ b/cpp/command/commandline.h @@ -12,7 +12,9 @@ class Logger; class KataGoCommandLine : public TCLAP::CmdLine { TCLAP::ValueArg* modelFileArg; + TCLAP::ValueArg* coreMLModelFileArg; TCLAP::ValueArg* humanModelFileArg; + TCLAP::ValueArg* humanCoreMLModelFileArg; TCLAP::MultiArg* configFileArg; TCLAP::MultiArg* overrideConfigArg; std::string defaultConfigFileName; @@ -31,7 +33,9 @@ class KataGoCommandLine : public TCLAP::CmdLine void setShortUsageArgLimit(); void addModelFileArg(); + void addCoreMLModelFileArg(); void addHumanModelFileArg(); + void addHumanCoreMLModelFileArg(); //Empty string indicates no default or no example void addConfigFileArg(const std::string& defaultConfigFileName, const std::string& exampleConfigFile); void addConfigFileArg(const std::string& defaultConfigFileName, const std::string& exampleConfigFile, bool required); @@ -40,9 +44,11 @@ class KataGoCommandLine : public TCLAP::CmdLine void logOverrides(Logger& logger) const; std::string getModelFile() const; + std::string getCoreMLModelFile() const; bool modelFileIsDefault() const; std::string getHumanModelFile() const; + std::string getHumanCoreMLModelFile() const; //cfg must be uninitialized, this will initialize it based on user-provided arguments void getConfig(ConfigParser& cfg) const; diff --git a/cpp/command/gatekeeper.cpp b/cpp/command/gatekeeper.cpp index e1a75c923..90f97cdd1 100644 --- a/cpp/command/gatekeeper.cpp +++ b/cpp/command/gatekeeper.cpp @@ -412,15 +412,15 @@ int MainCmds::gatekeeper(const vector& args) { const bool disableFP16 = false; const string expectedSha256 = ""; - NNEvaluator* testNNEval = Setup::initializeNNEvaluator( - testModelName,testModelFile,expectedSha256,cfg,logger,rand,expectedConcurrentEvals, + NNEvaluator* testNNEval = Setup::initializeCoreMLEvaluator( + testModelName,testModelFile,testModelDir,expectedSha256,cfg,logger,rand,expectedConcurrentEvals, maxBoardXSizeUsed,maxBoardYSizeUsed,defaultMaxBatchSize,defaultRequireExactNNLen,disableFP16, Setup::SETUP_FOR_OTHER ); logger.write("Loaded candidate neural net " + testModelName + " from: " + testModelFile); - NNEvaluator* acceptedNNEval = Setup::initializeNNEvaluator( - acceptedModelName,acceptedModelFile,expectedSha256,cfg,logger,rand,expectedConcurrentEvals, + NNEvaluator* acceptedNNEval = Setup::initializeCoreMLEvaluator( + acceptedModelName,acceptedModelFile,acceptedModelDir,expectedSha256,cfg,logger,rand,expectedConcurrentEvals, maxBoardXSizeUsed,maxBoardYSizeUsed,defaultMaxBatchSize,defaultRequireExactNNLen,disableFP16, Setup::SETUP_FOR_OTHER ); diff --git a/cpp/command/gtp.cpp b/cpp/command/gtp.cpp index 1a9169ea1..78a7da776 100644 --- a/cpp/command/gtp.cpp +++ b/cpp/command/gtp.cpp @@ -336,7 +336,9 @@ struct GTPEngine { GTPEngine& operator=(const GTPEngine&) = delete; const string nnModelFile; + const string coreMLModelFile; const string humanModelFile; + const string humanCoreMLModelFile; const bool assumeMultipleStartingBlackMovesAreHandicap; const int analysisPVLen; const bool preventEncore; @@ -386,7 +388,8 @@ struct GTPEngine { std::vector genmoveSamples; GTPEngine( - const string& modelFile, const string& hModelFile, + const string& modelFile, const string& coreMLModelFile, + const string& hModelFile, const string& hCoreMLModelFile, SearchParams initialGenmoveParams, SearchParams initialAnalysisParams, Rules initialRules, bool assumeMultiBlackHandicap, bool prevtEncore, bool autoPattern, @@ -397,7 +400,9 @@ struct GTPEngine { std::unique_ptr&& pbTable ) :nnModelFile(modelFile), + coreMLModelFile(coreMLModelFile), humanModelFile(hModelFile), + humanCoreMLModelFile(hCoreMLModelFile), assumeMultipleStartingBlackMovesAreHandicap(assumeMultiBlackHandicap), analysisPVLen(pvLen), preventEncore(prevtEncore), @@ -492,15 +497,15 @@ struct GTPEngine { const int defaultMaxBatchSize = std::max(8,((expectedConcurrentEvals+3)/4)*4); const bool disableFP16 = false; const string expectedSha256 = ""; - nnEval = Setup::initializeNNEvaluator( - nnModelFile,nnModelFile,expectedSha256,cfg,logger,seedRand,expectedConcurrentEvals, + nnEval = Setup::initializeCoreMLEvaluator( + nnModelFile,nnModelFile,coreMLModelFile,expectedSha256,cfg,logger,seedRand,expectedConcurrentEvals, nnXLen,nnYLen,defaultMaxBatchSize,defaultRequireExactNNLen,disableFP16, Setup::SETUP_FOR_GTP ); logger.write("Loaded neural net with nnXLen " + Global::intToString(nnEval->getNNXLen()) + " nnYLen " + Global::intToString(nnEval->getNNYLen())); if(humanModelFile != "") { - humanEval = Setup::initializeNNEvaluator( - humanModelFile,humanModelFile,expectedSha256,cfg,logger,seedRand,expectedConcurrentEvals, + humanEval = Setup::initializeCoreMLEvaluator( + humanModelFile,humanModelFile,humanCoreMLModelFile,expectedSha256,cfg,logger,seedRand,expectedConcurrentEvals, nnXLen,nnYLen,defaultMaxBatchSize,defaultRequireExactNNLen,disableFP16, Setup::SETUP_FOR_GTP ); @@ -1883,13 +1888,17 @@ int MainCmds::gtp(const vector& args) { ConfigParser cfg; string nnModelFile; + string coreMLModelFile; string humanModelFile; + string humanCoreMLModelFile; string overrideVersion; KataGoCommandLine cmd("Run KataGo main GTP engine for playing games or casual analysis."); try { cmd.addConfigFileArg(KataGoCommandLine::defaultGtpConfigFileName(),"gtp_example.cfg"); cmd.addModelFileArg(); + cmd.addCoreMLModelFileArg(); cmd.addHumanModelFileArg(); + cmd.addHumanCoreMLModelFileArg(); cmd.setShortUsageArgLimit(); cmd.addOverrideConfigArg(); @@ -1897,7 +1906,9 @@ int MainCmds::gtp(const vector& args) { cmd.add(overrideVersionArg); cmd.parseArgs(args); nnModelFile = cmd.getModelFile(); + coreMLModelFile = cmd.getCoreMLModelFile(); humanModelFile = cmd.getHumanModelFile(); + humanCoreMLModelFile = cmd.getHumanCoreMLModelFile(); overrideVersion = overrideVersionArg.getValue(); cmd.getConfig(cfg); @@ -2033,7 +2044,7 @@ int MainCmds::gtp(const vector& args) { Player perspective = Setup::parseReportAnalysisWinrates(cfg,C_EMPTY); GTPEngine* engine = new GTPEngine( - nnModelFile,humanModelFile, + nnModelFile,coreMLModelFile,humanModelFile,humanCoreMLModelFile, initialGenmoveParams,initialAnalysisParams, initialRules, assumeMultipleStartingBlackMovesAreHandicap,preventEncore,autoAvoidPatterns, diff --git a/cpp/command/selfplay.cpp b/cpp/command/selfplay.cpp index b03fb8c7d..75a597d99 100644 --- a/cpp/command/selfplay.cpp +++ b/cpp/command/selfplay.cpp @@ -162,8 +162,8 @@ int MainCmds::selfplay(const vector& args) { const string expectedSha256 = ""; Rand rand; - NNEvaluator* nnEval = Setup::initializeNNEvaluator( - modelName,modelFile,expectedSha256,cfg,logger,rand,expectedConcurrentEvals, + NNEvaluator* nnEval = Setup::initializeCoreMLEvaluator( + modelName,modelFile,modelDir,expectedSha256,cfg,logger,rand,expectedConcurrentEvals, maxBoardXSizeUsed,maxBoardYSizeUsed,defaultMaxBatchSize,defaultRequireExactNNLen,disableFP16, Setup::SETUP_FOR_OTHER ); diff --git a/cpp/configs/misc/coreml_analysis.cfg b/cpp/configs/misc/coreml_analysis.cfg new file mode 100644 index 000000000..b0455fece --- /dev/null +++ b/cpp/configs/misc/coreml_analysis.cfg @@ -0,0 +1,409 @@ +# Config for KataGo C++ Analysis engine, i.e. "./katago.exe analysis" + +# Example config for C++ (non-python) analysis engine + +# SEE NOTES ABOUT PERFORMANCE AND MEMORY USAGE IN gtp_example.cfg +# SEE NOTES ABOUT numSearchThreads AND OTHER IMPORTANT PARAMS BELOW! + +# Logs------------------------------------------------------------------------------------ + +# Where to output log? +logDir = analysis_logs # Each run of KataGo will log to a separate file in this dir +# logDirDated = analysis_logs # Use this instead of logDir to also write separate dated subdirs +# logFile = analysis.log # Use this instead of logDir to just specify a single file directly +# logToStderr = true # Echo everything output to log file to stderr as well +# logAllRequests = false # Log all input lines received to the analysis engine. +# logAllResponses = false # Log all lines output to stdout from the analysis engine. +# logErrorsAndWarnings = true # Log all lines output to stdout from the analysis engine that are errors and warnings +# logSearchInfo = false # Log debug info for every search performed + +# Analysis------------------------------------------------------------------------------------ + +# Controls the number of moves after the first move in a variation. +# analysisPVLen = 15 + +# Report winrates for analysis as (BLACK|WHITE|SIDETOMOVE). +reportAnalysisWinratesAs = BLACK + +# Larger values will make KataGo explore the top move(s) less deeply and accurately, +# but explore and give evaluations to a greater variety of moves. +# An extreme value like 1 will distribute many playouts across every move on the board, even very bad moves. +# NOTE: defaults to 0.04, under the presumption that the analysis engine will be used mainly for analysis. +# If you are intending to use the analysis engine to also play games and you want to maximize playing strength, +# set this to 0.0 either in this config or in the overrides. +# wideRootNoise = 0.04 + +# Bot behavior--------------------------------------------------------------------------------------- + +# Handicap ------------- + +# Assume that if black makes many moves in a row right at the start of the game, then the game is a handicap game. +# This is necessary on some servers and for some GUIs and also when initializing from many SGF files, which may +# set up a handicap game using repeated GTP "play" commands for black rather than GTP "place_free_handicap" commands. +# However, it may also lead to incorrect understanding of komi if whiteHandicapBonus is used and a server does NOT +# have such a practice. +# Defaults to true! Uncomment and set to false to disable this behavior. +# assumeMultipleStartingBlackMovesAreHandicap = true + +# Passing and cleanup ------------- + +# Make the bot never assume that its pass will end the game, even if passing would end and "win" under Tromp-Taylor rules. +# Usually this is a good idea when using it for analysis or playing on servers where scoring may be implemented non-tromp-taylorly. +# Defaults to true! Uncomment and set to false to disable this. +# conservativePass = true + +# When using territory scoring, self-play games continue beyond two passes with special cleanup +# rules that may be confusing for human players. This option prevents the special cleanup phases from being +# reachable when using the bot for GTP play. +# Defaults to true! Uncomment and set to false if you want KataGo to be able to enter special cleanup. +# For example, if you are testing it against itself, or against another bot that has precisely implemented the rules +# documented at https://lightvector.github.io/KataGo/rules.html +# preventCleanupPhase = true + +# Search limits----------------------------------------------------------------------------------- + +# By default, if NOT specified in an individual request, limit maximum number of root visits per search to this much +maxVisits = 500 +# If provided, cap search time at this many seconds +# maxTime = 60 + +# Search threads, batching, GPUs-------------------------------------------------------------------------- + +# Try a configuration like this if you only expect the engine to be handling a few queries at a time and you want +# individual queries to return more quickly, and are okay with the results being a bit lower-quality and the overall +# peak throughput on queries to be lower. +numAnalysisThreads = 2 +numSearchThreadsPerAnalysisThread = 32 + +# Try a configuration like this if you expect to be sending large numbers of queries at a time, and want to maximize +# total throughput and also the evaluation quality of all the queries and you never care about the response latency +# of the individual queries, only the throughput as a whole. +# numAnalysisThreads = 16 +# numSearchThreadsPerAnalysisThread = 2 + +# You will want to increase one or both numbers if you have a powerful GPU, and possibly decrease one or both if you +# have a very weak GPU, and play with the balance between them depending on your use case. +# Read the explanation below to understand how to set these parameters: + +# EXPLANATION: +# numAnalysisThreads: the number of POSITIONS to be able to search in parallel. +# numSearchThreadsPerAnalysisThread: the number of threads to use in the tree search for EACH position. +# (older analysis configs might just have 'numSearchThreads', this is an alias for 'numSearchThreadsPerAnalysisThread') + +# Therefore, the total number of search threads that may be active at a given time could be as large as the product: +# numAnalysisThreads * numSearchThreadsPerAnalysisThread + +# Searching more positions in parallel is more efficient since the different threads aren't conflicting with each +# other on the same MCTS search tree. Using multiple threads on the same search will both make things slower +# and weaken the search (holding playouts fixed) due to out of date statistics on nodes and suboptimal exploration, +# although the cost is minor for only 2,4,8 threads. + +# So unlike in GTP, which only ever searches one position at a time and where therefore you might as well make +# numSearchThreads as large as possible, in the analysis engine you often want you often want to keep numSearchThreads small, +# and instead parallelize across positions, so you can reduce conflict between threads and improve the overall throughput +# and strength of the search. + +# But obviously you only get the benefit of parallelization across positions when you actually have lots of positions +# that you are querying at once! For example, setting numAnalysisThreads = 8 is useless if you only ever send one or two +# queries at a time! + +# Therefore: +# * If you plan to use the analysis engine only for batch processing large numbers of positions, +# it's preferable to numSearchThreadsPerAnalysisThread to only a small number (e.g. 1,2,4) and use a higher numAnalysisThreads. +# * But if you sometimes plan to query the analysis engine for single positions, or otherwise in smaller quantities +# than -num-analysis-threads, or if you plan to be user-interactive such that the response time on some individual +# analysis requests is important to keep low, then set numSearchThreadsPerAnalysisThread to a larger number and use +# a lower numAnalysisThreads. That way, individual searches complete faster due to having more threads on each one. + +# For 19x19 boards, weaker GPUs probably want a TOTAL number of threads (numAnalysisThreads * numSearchThreadsPerAnalysisThread) +# between 4 and 32. Mid-tier GPUs probably between 16 and 64. Strong GPUs probably between 32 and 256. +# But there's no substitute for experimenting and seeing what's best for your hardware and your usage case. + +# Keep in mind that the number of threads you want does NOT necessarily have much to do with how many cores you have on your +# system. The optimal may easily exceed the number of cores! GPU batching is (usually) the dominant consideration. + +# ------------- + +# nnMaxBatchSize is the max number of positions to send to a single GPU at once. Generally, it should be the case that: +# (number of GPUs you will use * nnMaxBatchSize) >= (numSearchThreads * num-analysis-threads) +# That way, when each threads tries to request a GPU eval, your batch size summed across GPUs is large enough to handle them +# all at once. However, it can be sensible to set this a little smaller if you are limited on GPU memory, +# too large a number may fail if the GPU doesn't have enough memory. +nnMaxBatchSize = 16 + +# Uncomment and set these smaller if you are going to use the analysis engine EXCLUSIVELY for smaller boards (or plan to +# run multiple instances, with some instances only handling smaller boards). It should improve performance. +# It may also mean you can use more threads profitably. +# maxBoardXSizeForNNBuffer = 19 +# maxBoardYSizeForNNBuffer = 19 + +# Uncomment and set this to true if you are going to use the analysis engine EXCLUSIVELY for exactly the board size +# specified by maxBoardXSizeForNNBuffer and maxBoardYSizeForNNBuffer. It may slightly improve performance on some GPUs. +# requireMaxBoardSize = true + +# TO USE MULTIPLE GPUS: +# Metal + CoreML backends hack here. +# Metal backend runs the default GPU 0. +# CoreML backend runs at the other thread. +# So, if you want to use Metal + CoreML, you should set numNNServerThreadsPerModel to 2. +numNNServerThreadsPerModel = 2 + +# Other General GPU Settings------------------------------------------------------------------------------- + +# Cache up to 2 ** this many neural net evaluations in case of transpositions in the tree. +nnCacheSizePowerOfTwo = 23 +# Size of mutex pool for nnCache is 2 ** this +nnMutexPoolSizePowerOfTwo = 17 +# Randomize board orientation when running neural net evals? +nnRandomize = true + + +# TENSORRT GPU settings-------------------------------------- +# These only apply when using the TENSORRT version of KataGo. + +# IF USING ONE GPU: optionally uncomment and change this if the GPU you want to use turns out to be not device 0 +# trtDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines (AND set numNNServerThreadsPerModel above): +# trtDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# trtDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 + +# IF USING THREE GPUS: Uncomment these three lines (AND set numNNServerThreadsPerModel above): +# trtDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# trtDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 +# trtDeviceToUseThread2 = 2 # change this if the third GPU you want to use turns out to be not device 2 + +# You can probably guess the pattern if you have four, five, etc. GPUs. + + +# CUDA-specific GPU settings-------------------------------------- +# These only apply when using the CUDA version of KataGo. + +# IF USING ONE GPU: optionally uncomment and change this if the GPU you want to use turns out to be not device 0 +# cudaDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines (AND set numNNServerThreadsPerModel above): +# cudaDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# cudaDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 + +# IF USING THREE GPUS: Uncomment these three lines (AND set numNNServerThreadsPerModel above): +# cudaDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# cudaDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 +# cudaDeviceToUseThread2 = 2 # change this if the third GPU you want to use turns out to be not device 2 + +# You can probably guess the pattern if you have four, five, etc. GPUs. + +# KataGo will automatically use FP16 or not based on the compute capability of your NVIDIA GPU. If you +# want to try to force a particular behavior though you can uncomment these lines and change them +# to "true" or "false". E.g. it's using FP16 but on your card that's giving an error, or it's not using +# FP16 but you think it should. +# cudaUseFP16 = auto +# cudaUseNHWC = auto + + +# OpenCL-specific GPU settings-------------------------------------- +# These only apply when using the OpenCL version of KataGo. + +# Uncomment to tune OpenCL for every board size separately, rather than only the largest possible size +# openclReTunePerBoardSize = true + +# IF USING ONE GPU: optionally uncomment and change this if the best device to use is guessed incorrectly. +# The default behavior tries to guess the 'best' GPU or device on your system to use, usually it will be a good guess. +# openclDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines and replace X and Y with the device ids of the devices you want to use. +# It might NOT be 0 and 1, some computers will have many OpenCL devices. You can see what the devices are when +# KataGo starts up - it should print or log all the devices it finds. +# (AND also set numNNServerThreadsPerModel above) +# openclDeviceToUseThread0 = X +# openclDeviceToUseThread1 = Y + +# IF USING THREE GPUS: Uncomment these three lines and replace X and Y and Z with the device ids of the devices you want to use. +# It might NOT be 0 and 1 and 2, some computers will have many OpenCL devices. You can see what the devices are when +# KataGo starts up - it should print or log all the devices it finds. +# (AND also set numNNServerThreadsPerModel above) +# openclDeviceToUseThread0 = X +# openclDeviceToUseThread1 = Y +# openclDeviceToUseThread2 = Z + +# You can probably guess the pattern if you have four, five, etc. GPUs. + +# KataGo will automatically use FP16 or not based on testing your GPU during tuning. If you +# want to try to force a particular behavior though you can uncomment this lines and change it +# to "true" or "false". This is a fairly blunt setting - more detailed settings are testable +# by rerunning the tuner with various arguments. +# openclUseFP16 = auto + + +# Eigen-specific settings-------------------------------------- +# These only apply when using the Eigen (pure CPU) version of KataGo. + +# This is the number of CPU threads for evaluating the neural net on the Eigen backend. +# It defaults to min(numAnalysisThreads * numSearchThreadsPerAnalysisThread, numCPUCores). +# numEigenThreadsPerModel = X + +# CoreML settings-------------------------------------- +# These only apply when using the CoreML version of KataGo. + +# IF USING ONE MODEL: +# coremlDeviceToUse = 0 # GPU +# coremlDeviceToUse = 100 # Neural Engine + +# IF USING TWO MODEL: Uncomment these two lines +# (AND also set numNNServerThreadsPerModel = 2 above) +coremlDeviceToUseThread0 = 0 # GPU +coremlDeviceToUseThread1 = 100 # Neural Engine + +# IF USING THREE MODEL: Uncomment these three lines +# (AND also set numNNServerThreadsPerModel = 3 above) +# coremlDeviceToUseThread0 = 0 # GPU +# coremlDeviceToUseThread1 = 100 # Neural Engine +# coremlDeviceToUseThread2 = 101 # Neural Engine + +# Misc Behavior -------------------- + +# If the board is symmetric, search only one copy of each equivalent move. Attempts to also account for ko/superko, will not theoretically perfect for superko. +# Uncomment and set to false to disable this. +# rootSymmetryPruning = true + +# Uncomment and set to true to make KataGo avoid a particular joseki that some KataGo nets misevaluate, +# and also to improve opening diversity versus some particular other bots that like to play it all the time. +# avoidMYTDaggerHack = false + +# Have KataGo mildly prefer to avoid playing the same joseki in every corner of the board. +# Uncomment to set to a specific value. A small value like 0.005 should produce already a noticeable behavior change. +# avoidRepeatedPatternUtility = 0.0 + +# Enable some hacks that mitigate rare instances when passing messes up deeper searches. +# enablePassingHacks = true + +# Root move selection and biases------------------------------------------------------------------------------ +# Uncomment and edit any of the below values to change them from their default. +# Not all of these parameters are applicable to analysis, some are only used for actual play + +# Temperature for the early game, randomize between chosen moves with this temperature +# chosenMoveTemperatureEarly = 0.5 +# Decay temperature for the early game by 0.5 every this many moves, scaled with board size. +# chosenMoveTemperatureHalflife = 19 +# At the end of search after the early game, randomize between chosen moves with this temperature +# chosenMoveTemperature = 0.10 +# Subtract this many visits from each move prior to applying chosenMoveTemperature +# (unless all moves have too few visits) to downweight unlikely moves +# chosenMoveSubtract = 0 +# The same as chosenMoveSubtract but only prunes moves that fall below the threshold, does not affect moves above +# chosenMovePrune = 1 + +# Number of symmetries to sample (WITHOUT replacement) and average at the root +# rootNumSymmetriesToSample = 1 + +# Using LCB for move selection? +# useLcbForSelection = true +# How many stdevs a move needs to be better than another for LCB selection +# lcbStdevs = 5.0 +# Only use LCB override when a move has this proportion of visits as the top move +# minVisitPropForLCB = 0.15 + +# Internal params------------------------------------------------------------------------------ +# Uncomment and edit any of the below values to change them from their default. + +# Scales the utility of winning/losing +# winLossUtilityFactor = 1.0 +# Scales the utility for trying to maximize score +# staticScoreUtilityFactor = 0.10 +# dynamicScoreUtilityFactor = 0.30 +# Adjust dynamic score center this proportion of the way towards zero, capped at a reasonable amount. +# dynamicScoreCenterZeroWeight = 0.20 +# dynamicScoreCenterScale = 0.75 +# The utility of getting a "no result" due to triple ko or other long cycle in non-superko rulesets (-1 to 1) +# noResultUtilityForWhite = 0.0 +# The number of wins that a draw counts as, for white. (0 to 1) +# drawEquivalentWinsForWhite = 0.5 + +# Exploration constant for mcts +# cpuctExploration = 1.0 +# cpuctExplorationLog = 0.45 + +# Parameters that control exploring more in volatile positions, exploring less in stable positions. +# cpuctUtilityStdevPrior = 0.40 +# cpuctUtilityStdevPriorWeight = 2.0 +# cpuctUtilityStdevScale = 0.85 + +# FPU reduction constant for mcts +# fpuReductionMax = 0.2 +# rootFpuReductionMax = 0.1 +# fpuParentWeightByVisitedPolicy = true + +# Parameters that control weighting of evals based on the net's own self-reported uncertainty. +# useUncertainty = true +# uncertaintyExponent = 1.0 +# uncertaintyCoeff = 0.25 + +# Amount to apply a downweighting of children with very bad values relative to good ones +# valueWeightExponent = 0.25 + +# Slight incentive for the bot to behave human-like with regard to passing at the end, filling the dame, +# not wasting time playing in its own territory, etc, and not play moves that are equivalent in terms of +# points but a bit more unfriendly to humans. +# rootEndingBonusPoints = 0.5 + +# Make the bot prune useless moves that are just prolonging the game to avoid losing yet +# rootPruneUselessMoves = true + +# Apply bias correction based on local pattern keys +# subtreeValueBiasFactor = 0.45 +# subtreeValueBiasWeightExponent = 0.85 + +# Use graph search rather than tree search - identify and share search for transpositions. +# useGraphSearch = true + +# How much to shard the node table for search synchronization +# nodeTableShardsPowerOfTwo = 16 +# How many virtual losses to add when a thread descends through a node +# numVirtualLossesPerThread = 1 + +# Improve the quality of evals under heavy multithreading +# useNoisePruning = true + + +# Avoid SGF Patterns ------------------------------------------------------------------------------ +# The parameters in this section provide a powerful way to customize KataGo to avoid moves that follow specific patterns +# based on a set of provided SGF files loaded upon startup. Uncomment them to use this feature. +# Additionally, if the SGF file contains the string %SKIP% in a comment on a move, that move will be ignored for this purpose. + +# Load sgf files from this directory when the engine is started (ONLY on startup, will not reload unless engine is restarted) +# avoidSgfPatternDirs = path/to/directory/with/sgfs/ +# You can also surround the file path in double quotes if the file path contains trailing spaces or hash signs. +# Within double quotes, backslashes are escape characters. +# avoidSgfPatternDirs = "path/to/directory/with/sgfs/" + +# Penalize this much utility per matching move. +# Set this negative if you instead want to make KataGo favor the SGF patterns instead of penalizing it! +# This number does not need to be large, even 0.001 will make a difference. Too-large values may lead to bad play. +# avoidSgfPatternUtility = 0.001 + +# Optional - load only the newest this many files +# avoidSgfPatternMaxFiles = 20 + +# Optional - Penalty is multiplied by this per each older SGF file, so that old sgf files matter less than newer ones. +# avoidSgfPatternLambda = 0.90 + +# Optional - pay attention only to moves that were made by players with this name. +# For example you can set it to the name that your bot's past games will show up as in the SGF, so that the bot will only avoid repeating +# moves that itself made in past games, not the moves that its opponents made. +# avoidSgfPatternAllowedNames = my-ogs-bot-name1,my-ogs-bot-name2 + +# Optional - Ignore any moves in SGF files that occurred before this turn number. +# avoidSgfPatternMinTurnNumber = 0 + +# For more avoid patterns: +# You can also specify a second set of parameters, and a third, fourth, etc by numbering 2,3,4,... +# avoidSgf2PatternDirs = ... +# avoidSgf2PatternUtility = ... +# avoidSgf2PatternMaxFiles = ... +# avoidSgf2PatternLambda = ... +# avoidSgf2PatternAllowedNames = ... +# avoidSgf2PatternMinTurnNumber = ... + + + + diff --git a/cpp/configs/misc/coreml_example.cfg b/cpp/configs/misc/coreml_example.cfg new file mode 100644 index 000000000..71f26e7b9 --- /dev/null +++ b/cpp/configs/misc/coreml_example.cfg @@ -0,0 +1,496 @@ +# Config for KataGo C++ GTP engine, i.e. "./katago.exe gtp" + +# RUNNING ON AN ONLINE SERVER OR IN A REAL TOURNAMENT OR MATCH: +# If you plan to do so, you may want to read through the "Rules" section +# below carefully for proper handling of komi and handicap games and end-of-game cleanup +# and various other details. + +# NOTES ABOUT PERFORMANCE AND MEMORY USAGE: +# You will likely want to tune one or more the following: +# +# numSearchThreads: +# The number of CPU threads to use. If your GPU is powerful, it can actually be much higher than +# the number of cores on your processor because you will need many threads to feed large enough +# batches to make good use of the GPU. +# +# The "./katago benchmark" command can help you tune this parameter, as well as to test out the effect +# of changes to any of the other parameters below! +# +# nnCacheSizePowerOfTwo: +# This controls the NN Cache size, which is the primary RAM/memory use. +# Increase this if you don't mind the memory use and want better performance for searches with +# tens of thousands of visits or more. Decrease this if you want to limit memory usage. +# +# If you're someone who is happy to do a bit of math - each neural net entry takes very +# approximately 1.5KB, except when using whole-board ownership/territory visualizations, each +# entry will take very approximately 3KB. The number of entries is (2 ** nnCacheSizePowerOfTwo), +# for example 2 ** 18 = 262144. +# +# OTHER NOTES: +# If you have more than one GPU, take a look at "OpenCL GPU settings" or "CUDA GPU settings" below. +# +# If using OpenCL, you will want to verify that KataGo is picking up the correct device! +# (e.g. some systems may have both an Intel CPU OpenCL and GPU OpenCL, if KataGo appears to pick +# the wrong one, you correct this by specifying "openclGpuToUse" below). +# +# You may also want to adjust "maxVisits", "ponderingEnabled", "resignThreshold", and possibly +# other parameters depending on your intended usage. +# +# ---------------------------------------------------------------------------------------- + +# For the `katago gtp` command, ALL of THE BELOW VALUES MAY BE SET OR OVERRIDDEN if desired via +# the command line arguments: +# -override-config KEY=VALUE,KEY=VALUE,... + +# Logs and files-------------------------------------------------------------------------- + +# Where to output log? +logDir = gtp_logs # Each run of KataGo will log to a separate file in this dir +# logDirDated = gtp_logs # Use this instead of logDir to also write separate dated subdirs +# logFile = gtp.log # Use this instead of logDir to just specify a single file directly + +# Logging options +logAllGTPCommunication = true +logSearchInfo = true +logToStderr = false + +# KataGo will display some info to stderr on GTP startup +# Uncomment this to suppress that and remain silent +# startupPrintMessageToStderr = false + +# Chat some stuff to stderr, for use in things like malkovich chat to OGS. +# ogsChatToStderr = true + +# Optionally override where KataGo will attempt to save things like openCLTuner files and other cached data. +# homeDataDir = DIRECTORY + +# Analysis------------------------------------------------------------------------------------ + +# Configure the maximum length of analysis printed out by lz-analyze and other places. +# Controls the number of moves after the first move in a variation. +# analysisPVLen = 15 + +# Report winrates for chat and analysis as (BLACK|WHITE|SIDETOMOVE). +# Default is SIDETOMOVE, which is what tools that use LZ probably also expect +# reportAnalysisWinratesAs = SIDETOMOVE + +# Larger values will make KataGo explore the top move(s) less deeply and accurately, +# but explore and give evaluations to a greater variety of moves, for analysis (does NOT affect play). +# Defaults to 0.04. +# An extreme value like 1 will distribute many playouts across every move on the board, even very bad moves. +# analysisWideRootNoise = 0.04 + + +# Default rules------------------------------------------------------------------------------------ +# See https://lightvector.github.io/KataGo/rules.html for a description of the rules. +# These rules are defaults and can be changed mid-run by several custom GTP commands. +# See https://github.com/lightvector/KataGo/blob/master/docs/GTP_Extensions.md for those commands. + +# Some other legal values are: "chinese", "japanese", "korean", "aga", "chinese-ogs", "new-zealand". +# KataGo does not claim to exactly match any particular human ruleset, but KataGo will try to behave +# as closely as possible given the rules it has implemented. +rules = tromp-taylor + +# Use the below instead to specify an arbitrary combination of individual rules. + +# koRule = SIMPLE # Simple ko rules (triple ko = no result) +# koRule = POSITIONAL # Positional superko +# koRule = SITUATIONAL # Situational superko + +# scoringRule = AREA # Area scoring +# scoringRule = TERRITORY # Territory scoring (uses a sort of special computer-friendly territory ruleset) + +# taxRule = NONE # All surrounded empty points are scored +# taxRule = SEKI # Eyes in seki do NOT count as points +# taxRule = ALL # All groups are taxed up to 2 points for the two eyes needed to live + +# multiStoneSuicideLegal = true # Is multiple-stone suicide legal? (Single-stone suicide is always illegal). + +# hasButton = false # Set to true when area scoring to award 0.5 points to the first pass. + +# friendlyPassOk = true # Set to true except for computer rulesets that requires capturing all stones before passing. + +# whiteHandicapBonus = 0 # In handicap games, give white no compensation for black's handicap stones (Tromp-taylor, NZ, JP) +# whiteHandicapBonus = N-1 # In handicap games, give white N-1 points for black's handicap stones (AGA) +# whiteHandicapBonus = N # In handicap games, give white N points for black's handicap stones (Chinese) + +# Uncomment and change to adjust what board size KataGo uses upon startup by default if GTP doesn't specify. +# defaultBoardSize = 19 +# Specify this to force a particular komi, EVEN if the GUI or GTP controller tries to set a different one +# ignoreGTPAndForceKomi = 7 + +# Bot behavior--------------------------------------------------------------------------------------- + +# Resignation ------------- + +# Resignation occurs if for at least resignConsecTurns in a row, +# the winLossUtility (which is on a [-1,1] scale) is below resignThreshold. +allowResignation = true +resignThreshold = -0.99 +resignConsecTurns = 6 +# Uncomment to make katago not resign close games, behind by fewer than this many points +# resignMinScoreDifference = 10 + +# Handicap ------------- + +# Assume that if black makes many moves in a row right at the start of the game, then the game is a handicap game. +# This is necessary on some servers and for some GUIs and also when initializing from many SGF files, which may +# set up a handicap game using repeated GTP "play" commands for black rather than GTP "place_free_handicap" commands. +# However, it may also lead to incorrect understanding of komi if whiteHandicapBonus is used and a server does NOT +# have such a practice. +# Defaults to true! Uncomment and set to false to disable this behavior. +# assumeMultipleStartingBlackMovesAreHandicap = true + +# Makes katago dynamically adjust in handicap or altered-komi games to assume based on those game settings that it +# must be stronger or weaker than the opponent and to play accordingly. Greatly improves handicap +# strength by biasing winrates and scores to favor appropriate safe/aggressive play. +# Does NOT affect analysis (lz-analyze, kata-analyze, used by programs like Lizzie) so analysis remains unbiased. +# Uncomment and set this to 0 to disable this and make KataGo play the same always. +# dynamicPlayoutDoublingAdvantageCapPerOppLead = 0.045 + +# Instead of a dynamic level, you can uncomment this and set this to a value from -3.0 to 3.0 to set KataGo's aggression to a FIXED level. +# DOES affect analysis tools (lz-analyze, kata-analyze, used by programs like Lizzie). +# Negative makes KataGo behave as if it is much weaker than the opponent, preferring to play defensively. +# Positive makes KataGo behave as if it is much stronger than the opponent, prefering to play aggressively or even overplay slightly. +# If this and "dynamicPlayoutDoublingAdvantageCapPerOppLead" are BOTH set then dynamic will be used for all games and this fixed +# value will be used for analysis tools. +# playoutDoublingAdvantage = 0.0 + +# Uncommenting one of these will enforce that the FIXED playoutDoublingAdvantage will only apply when KataGo plays the specified color +# and will be negated when playing the opposite color. +# playoutDoublingAdvantagePla = BLACK +# playoutDoublingAdvantagePla = WHITE + +# Passing and cleanup ------------- + +# Make the bot never assume that its pass will end the game, even if passing would end and "win" under Tromp-Taylor rules. +# Usually this is a good idea when using it for analysis or playing on servers where scoring may be implemented non-tromp-taylorly. +# Defaults to true! Uncomment and set to false to disable this. +# conservativePass = true + +# When using territory scoring, self-play games continue beyond two passes with special cleanup +# rules that may be confusing for human players. This option prevents the special cleanup phases from being +# reachable when using the bot for GTP play. +# Defaults to true! Uncomment and set to false if you want KataGo to be able to enter special cleanup. +# For example, if you are testing it against itself, or against another bot that has precisely implemented the rules +# documented at https://lightvector.github.io/KataGo/rules.html +# preventCleanupPhase = true + +# Misc Behavior -------------------- + +# If the board is symmetric, search only one copy of each equivalent move. Attempts to also account for ko/superko, will not theoretically perfect for superko. +# Uncomment and set to false to disable this. +# rootSymmetryPruning = true + +# Uncomment and set to true to make KataGo avoid a particular joseki that some KataGo nets misevaluate, +# and also to improve opening diversity versus some particular other bots that like to play it all the time. +# avoidMYTDaggerHack = false + +# Have KataGo mildly prefer to avoid playing the same joseki in every corner of the board. +# Uncomment to set to a specific value. Otherwise, defaults to 0 in even games, and to 0.005 in handicap games. +# See also the Avoid SGF mechanism at the bottom of this config. +# avoidRepeatedPatternUtility = 0.0 + +# Experimental logic to make KataGo fight a bit against mirror Go even with unfavorable komi. +# Enabled by default for GTP play, disabled for GTP analysis (i.e lizzie) and analysis engine. +# Uncomment and set to true to enable it for analysis, or false to disable it fully. +# antiMirror = true + +# Search limits----------------------------------------------------------------------------------- + +# For all of "maxVisits", "maxPlayouts", "maxTime", search will still try to follow GTP time controls and may make a move +# faster than the specified max if GTP tells it that it is playing under a clock as well in the current game. + +# If provided, limit maximum number of root visits per search to this much. (With tree reuse, visits do count earlier search) +maxVisits = 500 +# If provided, limit maximum number of new playouts per search to this much. (With tree reuse, playouts do not count earlier search) +# maxPlayouts = 300 +# If provided, cap search time at this many seconds. +# maxTime = 10 + +# Ponder on the opponent's turn? +ponderingEnabled = false +maxTimePondering = 60 # Maximum time to ponder, in seconds. Comment out to make unlimited. +# Note: you can set "maxVisitsPondering" or "maxPlayoutsPondering" too. + +# Approx number of seconds to buffer for lag for GTP time controls - will move a bit faster assuming there is this much lag per move. +lagBuffer = 1.0 + +# Number of threads to use in search +numSearchThreads = 16 + +# Play a little faster if the opponent is passing, for friendliness +searchFactorAfterOnePass = 0.50 +searchFactorAfterTwoPass = 0.25 +# Play a little faster if super-winning, for friendliness +searchFactorWhenWinning = 0.40 +searchFactorWhenWinningThreshold = 0.95 + +# GPU Settings------------------------------------------------------------------------------- + +# Maximum number of positions to send to a single GPU at once. +# The default value here is roughly equal to numSearchThreads, but you can specify it manually +# if you are running out of memory, or if you are using multiple GPUs that expect to split +# up the work. +nnMaxBatchSize = 8 + +# Cache up to (2 ** this) many neural net evaluations in case of transpositions in the tree. +# Uncomment and edit to change if you want to adjust a major component of KataGo's RAM usage. +# nnCacheSizePowerOfTwo = 20 + +# Size of mutex pool for nnCache is (2 ** this). +# nnMutexPoolSizePowerOfTwo = 16 + +# Randomize board orientation when running neural net evals? Uncomment and set to false to disable. +# nnRandomize = true +# If provided, force usage of a specific seed for nnRandomize instead of randomizing. +# nnRandSeed = abcdefg + +# TO USE MULTIPLE GPUS: +numNNServerThreadsPerModel = 4 + + +# TENSORRT GPU settings-------------------------------------- +# These only apply when using the TENSORRT version of KataGo. + +# IF USING ONE GPU: optionally uncomment and change this if the GPU you want to use turns out to be not device 0 +# trtDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines (AND set numNNServerThreadsPerModel above): +# trtDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# trtDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 + +# IF USING THREE GPUS: Uncomment these three lines (AND set numNNServerThreadsPerModel above): +# trtDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# trtDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 +# trtDeviceToUseThread2 = 2 # change this if the third GPU you want to use turns out to be not device 2 + +# You can probably guess the pattern if you have four, five, etc. GPUs. + + +# CUDA GPU settings-------------------------------------- +# These only apply when using the CUDA version of KataGo. + +# IF USING ONE GPU: optionally uncomment and change this if the GPU you want to use turns out to be not device 0 +# cudaDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines (AND set numNNServerThreadsPerModel above): +# cudaDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# cudaDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 + +# IF USING THREE GPUS: Uncomment these three lines (AND set numNNServerThreadsPerModel above): +# cudaDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# cudaDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 +# cudaDeviceToUseThread2 = 2 # change this if the third GPU you want to use turns out to be not device 2 + +# You can probably guess the pattern if you have four, five, etc. GPUs. + +# KataGo will automatically use FP16 or not based on the compute capability of your NVIDIA GPU. If you +# want to try to force a particular behavior though you can uncomment these lines and change them +# to "true" or "false". E.g. it's using FP16 but on your card that's giving an error, or it's not using +# FP16 but you think it should. +# cudaUseFP16 = auto +# cudaUseNHWC = auto + + +# OpenCL GPU settings-------------------------------------- +# These only apply when using the OpenCL version of KataGo. + +# Uncomment to tune OpenCL for every board size separately, rather than only the largest possible size +# openclReTunePerBoardSize = true + +# IF USING ONE GPU: optionally uncomment and change this if the best device to use is guessed incorrectly. +# The default behavior tries to guess the 'best' GPU or device on your system to use, usually it will be a good guess. +# openclDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines and replace X and Y with the device ids of the devices you want to use. +# It might NOT be 0 and 1, some computers will have many OpenCL devices. You can see what the devices are when +# KataGo starts up - it should print or log all the devices it finds. +# (AND also set numNNServerThreadsPerModel above) +# openclDeviceToUseThread0 = X +# openclDeviceToUseThread1 = Y + +# IF USING THREE GPUS: Uncomment these three lines and replace X and Y and Z with the device ids of the devices you want to use. +# It might NOT be 0 and 1 and 2, some computers will have many OpenCL devices. You can see what the devices are when +# KataGo starts up - it should print or log all the devices it finds. +# (AND also set numNNServerThreadsPerModel above) +# openclDeviceToUseThread0 = X +# openclDeviceToUseThread1 = Y +# openclDeviceToUseThread2 = Z + +# You can probably guess the pattern if you have four, five, etc. GPUs. + +# KataGo will automatically use FP16 or not based on testing your GPU during tuning. If you +# want to try to force a particular behavior though you can uncomment this lines and change it +# to "true" or "false". This is a fairly blunt setting - more detailed settings are testable +# by rerunning the tuner with various arguments. +# openclUseFP16 = auto + + +# Eigen-specific settings-------------------------------------- +# These only apply when using the Eigen (pure CPU) version of KataGo. + +# This is the number of CPU threads for evaluating the neural net on the Eigen backend. +# It defaults to numSearchThreads. +# numEigenThreadsPerModel = X + +# CoreML settings-------------------------------------- +# These only apply when using the CoreML version of KataGo. + +# IF USING ONE MODEL: +# coremlDeviceToUse = 0 # GPU +# coremlDeviceToUse = 100 # Neural Engine + +# IF USING TWO MODEL: Uncomment these two lines +# (AND also set numNNServerThreadsPerModel = 2 above) +# coremlDeviceToUseThread0 = 0 # GPU +# coremlDeviceToUseThread1 = 100 # Neural Engine + +# IF USING THREE MODEL: Uncomment these three lines +# (AND also set numNNServerThreadsPerModel = 3 above) +# coremlDeviceToUseThread0 = 0 # GPU +# coremlDeviceToUseThread1 = 100 # Neural Engine +# coremlDeviceToUseThread2 = 101 # Neural Engine + +# IF USING FOUR MODEL: Uncomment these four lines +# (AND also set numNNServerThreadsPerModel = 4 above) +coremlDeviceToUseThread0 = 0 # GPU +coremlDeviceToUseThread1 = 1 # GPU +coremlDeviceToUseThread2 = 100 # Neural Engine +coremlDeviceToUseThread3 = 101 # Neural Engine + +# If you want to force the backend using float-point 16-bit or 32-bit, you can uncomment +# this lines and change it to "true" or "false". +# coremlUseFP16 = auto + +# You can probably guess the pattern if you have four, five, etc. Models. + +# Root move selection and biases------------------------------------------------------------------------------ +# Uncomment and edit any of the below values to change them from their default. + +# If provided, force usage of a specific seed for various things in the search instead of randomizing +# searchRandSeed = hijklmn + +# Temperature for the early game, randomize between chosen moves with this temperature +# chosenMoveTemperatureEarly = 0.5 +# Decay temperature for the early game by 0.5 every this many moves, scaled with board size. +# chosenMoveTemperatureHalflife = 19 +# At the end of search after the early game, randomize between chosen moves with this temperature +# chosenMoveTemperature = 0.10 +# Subtract this many visits from each move prior to applying chosenMoveTemperature +# (unless all moves have too few visits) to downweight unlikely moves +# chosenMoveSubtract = 0 +# The same as chosenMoveSubtract but only prunes moves that fall below the threshold, does not affect moves above +# chosenMovePrune = 1 + +# Number of symmetries to sample (WITHOUT replacement) and average at the root +# rootNumSymmetriesToSample = 1 + +# Using LCB for move selection? +# useLcbForSelection = true +# How many stdevs a move needs to be better than another for LCB selection +# lcbStdevs = 5.0 +# Only use LCB override when a move has this proportion of visits as the top move +# minVisitPropForLCB = 0.15 + +# Internal params------------------------------------------------------------------------------ +# Uncomment and edit any of the below values to change them from their default. + +# Scales the utility of winning/losing +# winLossUtilityFactor = 1.0 +# Scales the utility for trying to maximize score +# staticScoreUtilityFactor = 0.10 +# dynamicScoreUtilityFactor = 0.30 +# Adjust dynamic score center this proportion of the way towards zero, capped at a reasonable amount. +# dynamicScoreCenterZeroWeight = 0.20 +# dynamicScoreCenterScale = 0.75 +# The utility of getting a "no result" due to triple ko or other long cycle in non-superko rulesets (-1 to 1) +# noResultUtilityForWhite = 0.0 +# The number of wins that a draw counts as, for white. (0 to 1) +# drawEquivalentWinsForWhite = 0.5 + +# Exploration constant for mcts +# cpuctExploration = 1.0 +# cpuctExplorationLog = 0.45 + +# Parameters that control exploring more in volatile positions, exploring less in stable positions. +# cpuctUtilityStdevPrior = 0.40 +# cpuctUtilityStdevPriorWeight = 2.0 +# cpuctUtilityStdevScale = 0.85 + +# FPU reduction constant for mcts +# fpuReductionMax = 0.2 +# rootFpuReductionMax = 0.1 +# fpuParentWeightByVisitedPolicy = true + +# Parameters that control weighting of evals based on the net's own self-reported uncertainty. +# useUncertainty = true +# uncertaintyExponent = 1.0 +# uncertaintyCoeff = 0.25 + +# Amount to apply a downweighting of children with very bad values relative to good ones +# valueWeightExponent = 0.25 + +# Slight incentive for the bot to behave human-like with regard to passing at the end, filling the dame, +# not wasting time playing in its own territory, etc, and not play moves that are equivalent in terms of +# points but a bit more unfriendly to humans. +# rootEndingBonusPoints = 0.5 + +# Make the bot prune useless moves that are just prolonging the game to avoid losing yet +# rootPruneUselessMoves = true + +# Apply bias correction based on local pattern keys +# subtreeValueBiasFactor = 0.45 +# subtreeValueBiasWeightExponent = 0.85 + +# Use graph search rather than tree search - identify and share search for transpositions. +# useGraphSearch = true + +# How much to shard the node table for search synchronization +# nodeTableShardsPowerOfTwo = 16 +# How many virtual losses to add when a thread descends through a node +# numVirtualLossesPerThread = 1 + +# Improve the quality of evals under heavy multithreading +# useNoisePruning = true + + +# Avoid SGF Patterns ------------------------------------------------------------------------------ +# The parameters in this section provide a powerful way to customize KataGo to avoid moves that follow specific patterns +# based on a set of provided SGF files loaded upon startup. Uncomment them to use this feature. +# Additionally, if the SGF file contains the string %SKIP% in a comment on a move, that move will be ignored for this purpose. + +# Load sgf files from this directory when the engine is started (ONLY on startup, will not reload unless engine is restarted) +# avoidSgfPatternDirs = path/to/directory/with/sgfs/ + +# Penalize this much utility per matching move. +# Set this negative if you instead want to make KataGo favor the SGF patterns instead of penalizing it! +# This number does not need to be large, even 0.001 will make a difference. Too-large values may lead to bad play. +# avoidSgfPatternUtility = 0.001 + +# Optional - load only the newest this many files +# avoidSgfPatternMaxFiles = 20 + +# Optional - Penalty is multiplied by this per each older SGF file, so that old sgf files matter less than newer ones. +# avoidSgfPatternLambda = 0.90 + +# Optional - pay attention only to moves that were made by players with this name. +# For example you can set it to the name that your bot's past games will show up as in the SGF, so that the bot will only avoid repeating +# moves that itself made in past games, not the moves that its opponents made. +# avoidSgfPatternAllowedNames = my-ogs-bot-name1,my-ogs-bot-name2 + +# Optional - Ignore any moves in SGF files that occurred before this turn number. +# avoidSgfPatternMinTurnNumber = 0 + +# For more avoid patterns: +# You can also specify a second set of parameters, and a third, fourth, etc by numbering 2,3,4,... +# avoidSgf2PatternDirs = ... +# avoidSgf2PatternUtility = ... +# avoidSgf2PatternMaxFiles = ... +# avoidSgf2PatternLambda = ... +# avoidSgf2PatternAllowedNames = ... +# avoidSgf2PatternMinTurnNumber = ... + + + + diff --git a/cpp/configs/misc/coreml_gtp.cfg b/cpp/configs/misc/coreml_gtp.cfg new file mode 100644 index 000000000..8891f5385 --- /dev/null +++ b/cpp/configs/misc/coreml_gtp.cfg @@ -0,0 +1,492 @@ +# Config for KataGo C++ GTP engine, i.e. "./katago.exe gtp" + +# RUNNING ON AN ONLINE SERVER OR IN A REAL TOURNAMENT OR MATCH: +# If you plan to do so, you may want to read through the "Rules" section +# below carefully for proper handling of komi and handicap games and end-of-game cleanup +# and various other details. + +# NOTES ABOUT PERFORMANCE AND MEMORY USAGE: +# You will likely want to tune one or more the following: +# +# numSearchThreads: +# The number of CPU threads to use. If your GPU is powerful, it can actually be much higher than +# the number of cores on your processor because you will need many threads to feed large enough +# batches to make good use of the GPU. +# +# The "./katago benchmark" command can help you tune this parameter, as well as to test out the effect +# of changes to any of the other parameters below! +# +# nnCacheSizePowerOfTwo: +# This controls the NN Cache size, which is the primary RAM/memory use. +# Increase this if you don't mind the memory use and want better performance for searches with +# tens of thousands of visits or more. Decrease this if you want to limit memory usage. +# +# If you're someone who is happy to do a bit of math - each neural net entry takes very +# approximately 1.5KB, except when using whole-board ownership/territory visualizations, each +# entry will take very approximately 3KB. The number of entries is (2 ** nnCacheSizePowerOfTwo), +# for example 2 ** 18 = 262144. +# +# OTHER NOTES: +# If you have more than one GPU, take a look at "OpenCL GPU settings" or "CUDA GPU settings" below. +# +# If using OpenCL, you will want to verify that KataGo is picking up the correct device! +# (e.g. some systems may have both an Intel CPU OpenCL and GPU OpenCL, if KataGo appears to pick +# the wrong one, you correct this by specifying "openclGpuToUse" below). +# +# You may also want to adjust "maxVisits", "ponderingEnabled", "resignThreshold", and possibly +# other parameters depending on your intended usage. +# +# ---------------------------------------------------------------------------------------- + +# For the `katago gtp` command, ALL of THE BELOW VALUES MAY BE SET OR OVERRIDDEN if desired via +# the command line arguments: +# -override-config KEY=VALUE,KEY=VALUE,... + +# Logs and files-------------------------------------------------------------------------- + +# Where to output log? +logDir = gtp_logs # Each run of KataGo will log to a separate file in this dir +# logDirDated = gtp_logs # Use this instead of logDir to also write separate dated subdirs +# logFile = gtp.log # Use this instead of logDir to just specify a single file directly + +# Logging options +logAllGTPCommunication = true +logSearchInfo = true +logToStderr = false + +# KataGo will display some info to stderr on GTP startup +# Uncomment this to suppress that and remain silent +# startupPrintMessageToStderr = false + +# Chat some stuff to stderr, for use in things like malkovich chat to OGS. +# ogsChatToStderr = true + +# Optionally override where KataGo will attempt to save things like openCLTuner files and other cached data. +# homeDataDir = DIRECTORY + +# Analysis------------------------------------------------------------------------------------ + +# Configure the maximum length of analysis printed out by lz-analyze and other places. +# Controls the number of moves after the first move in a variation. +# analysisPVLen = 15 + +# Report winrates for chat and analysis as (BLACK|WHITE|SIDETOMOVE). +# Default is SIDETOMOVE, which is what tools that use LZ probably also expect +# reportAnalysisWinratesAs = SIDETOMOVE + +# Larger values will make KataGo explore the top move(s) less deeply and accurately, +# but explore and give evaluations to a greater variety of moves, for analysis (does NOT affect play). +# Defaults to 0.04. +# An extreme value like 1 will distribute many playouts across every move on the board, even very bad moves. +# analysisWideRootNoise = 0.04 + + +# Default rules------------------------------------------------------------------------------------ +# See https://lightvector.github.io/KataGo/rules.html for a description of the rules. +# These rules are defaults and can be changed mid-run by several custom GTP commands. +# See https://github.com/lightvector/KataGo/blob/master/docs/GTP_Extensions.md for those commands. + +# Some other legal values are: "chinese", "japanese", "korean", "aga", "chinese-ogs", "new-zealand". +# KataGo does not claim to exactly match any particular human ruleset, but KataGo will try to behave +# as closely as possible given the rules it has implemented. +rules = tromp-taylor + +# Use the below instead to specify an arbitrary combination of individual rules. + +# koRule = SIMPLE # Simple ko rules (triple ko = no result) +# koRule = POSITIONAL # Positional superko +# koRule = SITUATIONAL # Situational superko + +# scoringRule = AREA # Area scoring +# scoringRule = TERRITORY # Territory scoring (uses a sort of special computer-friendly territory ruleset) + +# taxRule = NONE # All surrounded empty points are scored +# taxRule = SEKI # Eyes in seki do NOT count as points +# taxRule = ALL # All groups are taxed up to 2 points for the two eyes needed to live + +# multiStoneSuicideLegal = true # Is multiple-stone suicide legal? (Single-stone suicide is always illegal). + +# hasButton = false # Set to true when area scoring to award 0.5 points to the first pass. + +# friendlyPassOk = true # Set to true except for computer rulesets that requires capturing all stones before passing. + +# whiteHandicapBonus = 0 # In handicap games, give white no compensation for black's handicap stones (Tromp-taylor, NZ, JP) +# whiteHandicapBonus = N-1 # In handicap games, give white N-1 points for black's handicap stones (AGA) +# whiteHandicapBonus = N # In handicap games, give white N points for black's handicap stones (Chinese) + +# Uncomment and change to adjust what board size KataGo uses upon startup by default if GTP doesn't specify. +# defaultBoardSize = 19 +# Specify this to force a particular komi, EVEN if the GUI or GTP controller tries to set a different one +# ignoreGTPAndForceKomi = 7 + +# Bot behavior--------------------------------------------------------------------------------------- + +# Resignation ------------- + +# Resignation occurs if for at least resignConsecTurns in a row, +# the winLossUtility (which is on a [-1,1] scale) is below resignThreshold. +allowResignation = true +resignThreshold = -0.99 +resignConsecTurns = 6 +# Uncomment to make katago not resign close games, behind by fewer than this many points +# resignMinScoreDifference = 10 + +# Handicap ------------- + +# Assume that if black makes many moves in a row right at the start of the game, then the game is a handicap game. +# This is necessary on some servers and for some GUIs and also when initializing from many SGF files, which may +# set up a handicap game using repeated GTP "play" commands for black rather than GTP "place_free_handicap" commands. +# However, it may also lead to incorrect understanding of komi if whiteHandicapBonus is used and a server does NOT +# have such a practice. +# Defaults to true! Uncomment and set to false to disable this behavior. +# assumeMultipleStartingBlackMovesAreHandicap = true + +# Makes katago dynamically adjust in handicap or altered-komi games to assume based on those game settings that it +# must be stronger or weaker than the opponent and to play accordingly. Greatly improves handicap +# strength by biasing winrates and scores to favor appropriate safe/aggressive play. +# Does NOT affect analysis (lz-analyze, kata-analyze, used by programs like Lizzie) so analysis remains unbiased. +# Uncomment and set this to 0 to disable this and make KataGo play the same always. +# dynamicPlayoutDoublingAdvantageCapPerOppLead = 0.045 + +# Instead of a dynamic level, you can uncomment this and set this to a value from -3.0 to 3.0 to set KataGo's aggression to a FIXED level. +# DOES affect analysis tools (lz-analyze, kata-analyze, used by programs like Lizzie). +# Negative makes KataGo behave as if it is much weaker than the opponent, preferring to play defensively. +# Positive makes KataGo behave as if it is much stronger than the opponent, prefering to play aggressively or even overplay slightly. +# If this and "dynamicPlayoutDoublingAdvantageCapPerOppLead" are BOTH set then dynamic will be used for all games and this fixed +# value will be used for analysis tools. +# playoutDoublingAdvantage = 0.0 + +# Uncommenting one of these will enforce that the FIXED playoutDoublingAdvantage will only apply when KataGo plays the specified color +# and will be negated when playing the opposite color. +# playoutDoublingAdvantagePla = BLACK +# playoutDoublingAdvantagePla = WHITE + +# Passing and cleanup ------------- + +# Make the bot never assume that its pass will end the game, even if passing would end and "win" under Tromp-Taylor rules. +# Usually this is a good idea when using it for analysis or playing on servers where scoring may be implemented non-tromp-taylorly. +# Defaults to true! Uncomment and set to false to disable this. +# conservativePass = true + +# When using territory scoring, self-play games continue beyond two passes with special cleanup +# rules that may be confusing for human players. This option prevents the special cleanup phases from being +# reachable when using the bot for GTP play. +# Defaults to true! Uncomment and set to false if you want KataGo to be able to enter special cleanup. +# For example, if you are testing it against itself, or against another bot that has precisely implemented the rules +# documented at https://lightvector.github.io/KataGo/rules.html +# preventCleanupPhase = true + +# Misc Behavior -------------------- + +# If the board is symmetric, search only one copy of each equivalent move. Attempts to also account for ko/superko, will not theoretically perfect for superko. +# Uncomment and set to false to disable this. +# rootSymmetryPruning = true + +# Uncomment and set to true to make KataGo avoid a particular joseki that some KataGo nets misevaluate, +# and also to improve opening diversity versus some particular other bots that like to play it all the time. +# avoidMYTDaggerHack = false + +# Have KataGo mildly prefer to avoid playing the same joseki in every corner of the board. +# Uncomment to set to a specific value. Otherwise, defaults to 0 in even games, and to 0.005 in handicap games. +# See also the Avoid SGF mechanism at the bottom of this config. +# avoidRepeatedPatternUtility = 0.0 + +# Experimental logic to make KataGo fight a bit against mirror Go even with unfavorable komi. +# Enabled by default for GTP play, disabled for GTP analysis (i.e lizzie) and analysis engine. +# Uncomment and set to true to enable it for analysis, or false to disable it fully. +# antiMirror = true + +# Search limits----------------------------------------------------------------------------------- + +# For all of "maxVisits", "maxPlayouts", "maxTime", search will still try to follow GTP time controls and may make a move +# faster than the specified max if GTP tells it that it is playing under a clock as well in the current game. + +# If provided, limit maximum number of root visits per search to this much. (With tree reuse, visits do count earlier search) +maxVisits = 500 +# If provided, limit maximum number of new playouts per search to this much. (With tree reuse, playouts do not count earlier search) +# maxPlayouts = 300 +# If provided, cap search time at this many seconds. +# maxTime = 10 + +# Ponder on the opponent's turn? +ponderingEnabled = false +maxTimePondering = 60 # Maximum time to ponder, in seconds. Comment out to make unlimited. +# Note: you can set "maxVisitsPondering" or "maxPlayoutsPondering" too. + +# Approx number of seconds to buffer for lag for GTP time controls - will move a bit faster assuming there is this much lag per move. +lagBuffer = 1.0 + +# Number of threads to use in search +numSearchThreads = 32 + +# Play a little faster if the opponent is passing, for friendliness +searchFactorAfterOnePass = 0.50 +searchFactorAfterTwoPass = 0.25 +# Play a little faster if super-winning, for friendliness +searchFactorWhenWinning = 0.40 +searchFactorWhenWinningThreshold = 0.95 + +# GPU Settings------------------------------------------------------------------------------- + +# Maximum number of positions to send to a single GPU at once. +# The default value here is roughly equal to numSearchThreads, but you can specify it manually +# if you are running out of memory, or if you are using multiple GPUs that expect to split +# up the work. +nnMaxBatchSize = 16 + +# Cache up to (2 ** this) many neural net evaluations in case of transpositions in the tree. +# Uncomment and edit to change if you want to adjust a major component of KataGo's RAM usage. +# nnCacheSizePowerOfTwo = 20 + +# Size of mutex pool for nnCache is (2 ** this). +# nnMutexPoolSizePowerOfTwo = 16 + +# Randomize board orientation when running neural net evals? Uncomment and set to false to disable. +# nnRandomize = true +# If provided, force usage of a specific seed for nnRandomize instead of randomizing. +# nnRandSeed = abcdefg + +# TO USE MULTIPLE GPUS: +# Metal + CoreML backends hack here. +# Metal backend runs the default GPU 0. +# CoreML backend runs at the other thread. +# So, if you want to use Metal + CoreML, you should set numNNServerThreadsPerModel to 2. +numNNServerThreadsPerModel = 1 + + +# TENSORRT GPU settings-------------------------------------- +# These only apply when using the TENSORRT version of KataGo. + +# IF USING ONE GPU: optionally uncomment and change this if the GPU you want to use turns out to be not device 0 +# trtDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines (AND set numNNServerThreadsPerModel above): +# trtDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# trtDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 + +# IF USING THREE GPUS: Uncomment these three lines (AND set numNNServerThreadsPerModel above): +# trtDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# trtDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 +# trtDeviceToUseThread2 = 2 # change this if the third GPU you want to use turns out to be not device 2 + +# You can probably guess the pattern if you have four, five, etc. GPUs. + + +# CUDA GPU settings-------------------------------------- +# These only apply when using the CUDA version of KataGo. + +# IF USING ONE GPU: optionally uncomment and change this if the GPU you want to use turns out to be not device 0 +# cudaDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines (AND set numNNServerThreadsPerModel above): +# cudaDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# cudaDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 + +# IF USING THREE GPUS: Uncomment these three lines (AND set numNNServerThreadsPerModel above): +# cudaDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# cudaDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 +# cudaDeviceToUseThread2 = 2 # change this if the third GPU you want to use turns out to be not device 2 + +# You can probably guess the pattern if you have four, five, etc. GPUs. + +# KataGo will automatically use FP16 or not based on the compute capability of your NVIDIA GPU. If you +# want to try to force a particular behavior though you can uncomment these lines and change them +# to "true" or "false". E.g. it's using FP16 but on your card that's giving an error, or it's not using +# FP16 but you think it should. +# cudaUseFP16 = auto +# cudaUseNHWC = auto + + +# OpenCL GPU settings-------------------------------------- +# These only apply when using the OpenCL version of KataGo. + +# Uncomment to tune OpenCL for every board size separately, rather than only the largest possible size +# openclReTunePerBoardSize = true + +# IF USING ONE GPU: optionally uncomment and change this if the best device to use is guessed incorrectly. +# The default behavior tries to guess the 'best' GPU or device on your system to use, usually it will be a good guess. +# openclDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines and replace X and Y with the device ids of the devices you want to use. +# It might NOT be 0 and 1, some computers will have many OpenCL devices. You can see what the devices are when +# KataGo starts up - it should print or log all the devices it finds. +# (AND also set numNNServerThreadsPerModel above) +# openclDeviceToUseThread0 = X +# openclDeviceToUseThread1 = Y + +# IF USING THREE GPUS: Uncomment these three lines and replace X and Y and Z with the device ids of the devices you want to use. +# It might NOT be 0 and 1 and 2, some computers will have many OpenCL devices. You can see what the devices are when +# KataGo starts up - it should print or log all the devices it finds. +# (AND also set numNNServerThreadsPerModel above) +# openclDeviceToUseThread0 = X +# openclDeviceToUseThread1 = Y +# openclDeviceToUseThread2 = Z + +# You can probably guess the pattern if you have four, five, etc. GPUs. + +# KataGo will automatically use FP16 or not based on testing your GPU during tuning. If you +# want to try to force a particular behavior though you can uncomment this lines and change it +# to "true" or "false". This is a fairly blunt setting - more detailed settings are testable +# by rerunning the tuner with various arguments. +# openclUseFP16 = auto + + +# Eigen-specific settings-------------------------------------- +# These only apply when using the Eigen (pure CPU) version of KataGo. + +# This is the number of CPU threads for evaluating the neural net on the Eigen backend. +# It defaults to numSearchThreads. +# numEigenThreadsPerModel = X + +# CoreML settings-------------------------------------- +# These only apply when using the CoreML version of KataGo. + +# IF USING ONE MODEL: +coremlDeviceToUse = 100 # Neural Engine + +# IF USING TWO MODEL: Uncomment these two lines +# (AND also set numNNServerThreadsPerModel = 2 above) +# coremlDeviceToUseThread0 = 0 # GPU +# coremlDeviceToUseThread1 = 100 # Neural Engine + +# IF USING THREE MODEL: Uncomment these three lines +# (AND also set numNNServerThreadsPerModel = 3 above) +# coremlDeviceToUseThread0 = 0 # GPU +# coremlDeviceToUseThread1 = 100 # Neural Engine +# coremlDeviceToUseThread2 = 101 # Neural Engine + +# If you want to force the backend using float-point 16-bit or 32-bit, you can uncomment +# this lines and change it to "true" or "false". +# coremlUseFP16 = auto + +# You can probably guess the pattern if you have four, five, etc. Models. + +# Root move selection and biases------------------------------------------------------------------------------ +# Uncomment and edit any of the below values to change them from their default. + +# If provided, force usage of a specific seed for various things in the search instead of randomizing +# searchRandSeed = hijklmn + +# Temperature for the early game, randomize between chosen moves with this temperature +# chosenMoveTemperatureEarly = 0.5 +# Decay temperature for the early game by 0.5 every this many moves, scaled with board size. +# chosenMoveTemperatureHalflife = 19 +# At the end of search after the early game, randomize between chosen moves with this temperature +# chosenMoveTemperature = 0.10 +# Subtract this many visits from each move prior to applying chosenMoveTemperature +# (unless all moves have too few visits) to downweight unlikely moves +# chosenMoveSubtract = 0 +# The same as chosenMoveSubtract but only prunes moves that fall below the threshold, does not affect moves above +# chosenMovePrune = 1 + +# Number of symmetries to sample (WITHOUT replacement) and average at the root +# rootNumSymmetriesToSample = 1 + +# Using LCB for move selection? +# useLcbForSelection = true +# How many stdevs a move needs to be better than another for LCB selection +# lcbStdevs = 5.0 +# Only use LCB override when a move has this proportion of visits as the top move +# minVisitPropForLCB = 0.15 + +# Internal params------------------------------------------------------------------------------ +# Uncomment and edit any of the below values to change them from their default. + +# Scales the utility of winning/losing +# winLossUtilityFactor = 1.0 +# Scales the utility for trying to maximize score +# staticScoreUtilityFactor = 0.10 +# dynamicScoreUtilityFactor = 0.30 +# Adjust dynamic score center this proportion of the way towards zero, capped at a reasonable amount. +# dynamicScoreCenterZeroWeight = 0.20 +# dynamicScoreCenterScale = 0.75 +# The utility of getting a "no result" due to triple ko or other long cycle in non-superko rulesets (-1 to 1) +# noResultUtilityForWhite = 0.0 +# The number of wins that a draw counts as, for white. (0 to 1) +# drawEquivalentWinsForWhite = 0.5 + +# Exploration constant for mcts +# cpuctExploration = 1.0 +# cpuctExplorationLog = 0.45 + +# Parameters that control exploring more in volatile positions, exploring less in stable positions. +# cpuctUtilityStdevPrior = 0.40 +# cpuctUtilityStdevPriorWeight = 2.0 +# cpuctUtilityStdevScale = 0.85 + +# FPU reduction constant for mcts +# fpuReductionMax = 0.2 +# rootFpuReductionMax = 0.1 +# fpuParentWeightByVisitedPolicy = true + +# Parameters that control weighting of evals based on the net's own self-reported uncertainty. +# useUncertainty = true +# uncertaintyExponent = 1.0 +# uncertaintyCoeff = 0.25 + +# Amount to apply a downweighting of children with very bad values relative to good ones +# valueWeightExponent = 0.25 + +# Slight incentive for the bot to behave human-like with regard to passing at the end, filling the dame, +# not wasting time playing in its own territory, etc, and not play moves that are equivalent in terms of +# points but a bit more unfriendly to humans. +# rootEndingBonusPoints = 0.5 + +# Make the bot prune useless moves that are just prolonging the game to avoid losing yet +# rootPruneUselessMoves = true + +# Apply bias correction based on local pattern keys +# subtreeValueBiasFactor = 0.45 +# subtreeValueBiasWeightExponent = 0.85 + +# Use graph search rather than tree search - identify and share search for transpositions. +# useGraphSearch = true + +# How much to shard the node table for search synchronization +# nodeTableShardsPowerOfTwo = 16 +# How many virtual losses to add when a thread descends through a node +# numVirtualLossesPerThread = 1 + +# Improve the quality of evals under heavy multithreading +# useNoisePruning = true + + +# Avoid SGF Patterns ------------------------------------------------------------------------------ +# The parameters in this section provide a powerful way to customize KataGo to avoid moves that follow specific patterns +# based on a set of provided SGF files loaded upon startup. Uncomment them to use this feature. +# Additionally, if the SGF file contains the string %SKIP% in a comment on a move, that move will be ignored for this purpose. + +# Load sgf files from this directory when the engine is started (ONLY on startup, will not reload unless engine is restarted) +# avoidSgfPatternDirs = path/to/directory/with/sgfs/ + +# Penalize this much utility per matching move. +# Set this negative if you instead want to make KataGo favor the SGF patterns instead of penalizing it! +# This number does not need to be large, even 0.001 will make a difference. Too-large values may lead to bad play. +# avoidSgfPatternUtility = 0.001 + +# Optional - load only the newest this many files +# avoidSgfPatternMaxFiles = 20 + +# Optional - Penalty is multiplied by this per each older SGF file, so that old sgf files matter less than newer ones. +# avoidSgfPatternLambda = 0.90 + +# Optional - pay attention only to moves that were made by players with this name. +# For example you can set it to the name that your bot's past games will show up as in the SGF, so that the bot will only avoid repeating +# moves that itself made in past games, not the moves that its opponents made. +# avoidSgfPatternAllowedNames = my-ogs-bot-name1,my-ogs-bot-name2 + +# Optional - Ignore any moves in SGF files that occurred before this turn number. +# avoidSgfPatternMinTurnNumber = 0 + +# For more avoid patterns: +# You can also specify a second set of parameters, and a third, fourth, etc by numbering 2,3,4,... +# avoidSgf2PatternDirs = ... +# avoidSgf2PatternUtility = ... +# avoidSgf2PatternMaxFiles = ... +# avoidSgf2PatternLambda = ... +# avoidSgf2PatternAllowedNames = ... +# avoidSgf2PatternMinTurnNumber = ... + + + + diff --git a/cpp/configs/misc/gtp_human5k_coreml.cfg b/cpp/configs/misc/gtp_human5k_coreml.cfg new file mode 100644 index 000000000..7a172aea6 --- /dev/null +++ b/cpp/configs/misc/gtp_human5k_coreml.cfg @@ -0,0 +1,201 @@ + +# This is an example config for configuring KataGo to attempt to imitate a weaker human player. +# Running with this config requires giving a human SL model b18c384nbt-humanv0.bin.gz +# on the command line such as: +# ./katago gtp -config gtp_human5k_example.cfg -model your_favorite_normal_model_for_katago.bin.gz -human-model b18c384nbt-humanv0.bin.gz +# You can obtain the human model at https://github.com/lightvector/KataGo/releases/tag/v1.15.0 + +# Below, the most important parts of the config for human-like play are commented. +# See the original gtp_example for comments on other parameters. + +# For another useful guide on human-style analysis, see here: +# https://github.com/lightvector/KataGo/blob/master/docs/Analysis_Engine.md#human-sl-analysis-guide + +# It is ALSO possible to pass in simply '-model b18c384nbt-humanv0.bin.gz' and NOT +# pass in -human-model, i.e. use the human model as if it were KataGo's normal neural net. +# If you do that, you need to use a config more like the normal gtp_example.cfg, not this config! +# Keep in mind that if using the model normally, or if using -human-model but also altering +# parameters below to blend in some of KataGo's search, KataGo's play might NOT be very human-like, +# or might be human-like but play at a strength very different than the humanSLProfile. +# You can experiment, some of the comments below hopefully will help illustrate things too. + +logDir = gtp_logs +logAllGTPCommunication = true +logSearchInfo = true +logSearchInfoForChosenMove = false +logToStderr = false + +# Use these rules by default, but a GUI or GTP controller might override this. +rules = japanese + +# When using -human-model, we only resign when far behind since a weaker player +# might continue to fight much longer than a strong bot normally would. +allowResignation = true +resignThreshold = -0.99 +resignConsecTurns = 20 +resignMinScoreDifference = 40 +resignMinMovesPerBoardArea = 0.4 + +# Note: unless you change other parameters too, by default increasing visits won't do much. +# If humanSLChosenMoveProp = 1.0 AND humanSLChosenMovePiklLambda is a large number, +# then KataGo's normal search is ignored except for possibly choosing whether to pass/resign, +# so more visits will have no effect on play. Still, having some visits is good for +# ensuring good pass/resign judgment. +maxVisits = 40 +numSearchThreads = 1 +lagBuffer = 1.0 + +# Rough scale in seconds to randomly delay moving, so as not to respond instantly. +# Some moves will delay longer, some moves will delay a little less. +delayMoveScale = 2.0 +delayMoveMax = 10.0 + +# =========================================================================== +# HUMAN SL PARAMETERS +# =========================================================================== + +# The most important parameter for human-like play configuration! +# Choose the "profile" of players that the human SL model will imitate. +# Available options are: +# preaz_{RANK from 20k to 9d} - imitate player of given rank, before AlphaZero opening style became popular +# rank_{RANK from 20k to 9d} - imitate player of given rank, after human openings changed due to AlphaZero. +# preaz_{BRANK}_{WRANK} or rank_{BRANK}_{WRANK} - same, but imitate how black with the rank BR and white +# with the rank WR would play against each other, knowing that the other player is stronger/weaker than them. +# Warning: for rank differences > 9 ranks, or drastically mis-matched to the handicap used in the game, +# this may be out of distribution due to lack of training data and the model might not behave well! Experiment with care. +# proyear_{YEAR from 1800 to 2023} - imitate historical pros or insei from given year. +humanSLProfile = preaz_5k + +# The probability that we should play a HUMAN-like move, rather than playing KataGo's move. +# Applies BEFORE temperature. +humanSLChosenMoveProp = 1.0 + +# If true, ignore the human SL model's choice of when to pass, and still use KataGo to determine that. +# The human SL model, in theory, is not guaranteed to be reliable at when to pass for all profiles, +# since e.g. some historical games omit passes. +humanSLChosenMoveIgnorePass = true + +# By default humanSLChosenMovePiklLambda is a large number which effectively disables it. +# Setting it to a smaller number will "suppress" human-like moves that KataGo disapproves of. +# In particular, if set to, for example, 0.4 when KataGo judges a human SL move to lose 0.4 utility, +# it will substantially suppress the chance of playing that move (in particular, by a factor of exp(1)). +# Less-bad moves will also be suppressed, but not by as much, e.g. a move losing 0.2 would get lowered +# by a factor of exp(0.5). +# As configured lower down, utilities by default range from -1.0 (loss) to +1.0 (win), plus up to +/- 0.3 for score. +# WARNING: ONLY moves that KataGo actually searches will get suppressed! If a move is so bad that KataGo +# rejects it without searching it, it will NOT get suppressed. +# Therefore, to use humanSLChosenMovePiklLambda, it is STRONGLY recommended that you also use something +# like humanSLRootExploreProbWeightless to ensure most human moves including bad moves get searched, +# and ALSO use at least hundreds and ideally thousands of maxVisits, to ensure enough visits. +humanSLChosenMovePiklLambda = 100000000 + +# These parameters tell KataGo to use the human SL policy for exploration during search. +# Each of these specifies the probability that KataGo will perform PUCT using the Human SL policy to +# explore different moves, rather than using KataGo's normal policy, after a certain minimal number of visits. +# "Root": applies only at the root of the search +# "Pla": applies during non-root nodes of the search where it is katago's turn. +# "Opp": applies during non-root nodes of the search where it is the opponent's turn. +# "Weightless": search the move to evaluate it, but do NOT allow this visit to affect the parent's average utility. +# "Weightful": search the move to evaluate it, and DO allow this visit to affect the parent's average utility. +# For example, humanSLRootExploreProbWeightless = 0.5 would tell KataGo at the root of the search to spend +# 50% of its visits to judge different possible human moves, but NOT to use those visits for determining the +# value of the position (avoiding biasing the utility if some human SL moves are very bad). +# If you don't understand these well, ask for help or look up some online explainers for MCTS (Monte-Carlo Tree Search). +humanSLRootExploreProbWeightless = 0.0 +humanSLRootExploreProbWeightful = 0.0 +humanSLPlaExploreProbWeightless = 0.0 +humanSLPlaExploreProbWeightful = 0.0 +humanSLOppExploreProbWeightless = 0.0 +humanSLOppExploreProbWeightful = 0.0 + +# When using the human SL policy for exploration during search, use this cPUCT. +# This only has an effect if at least one of humanSL{Root,Pla,Opp}ExploreProbWeight{less,ful} is nonzero. +humanSLCpuctExploration = 0.50 + +# Same as humanSLCpuctExploration, but NEVER diminshes its exploration no matter how many visits are used. +# Normally, PUCT will sharpen with visits and spend a diminishing proportion of visits on moves with lower utility. +# This is the coefficient for a term that does NOT diminish, i.e. if this is 0.2, then roughly moves within +# 0.2 utility (about 10% winrate) of the best move will forever continue getting a decent fraction of visits, +# smoothly falling off for greater utility differences. +# Note that in combination with Weightful exploration above, if used for Opp exploration, this could be used +# to model an opponent that will always have some chance to make small mistakes no matter how deep they search. +# If further this was increased to a very large value, it would model an opponent that always played according +# to the human SL raw policy. These might be interesting to experiment with for handicap play. +humanSLCpuctPermanent = 0.2 + + +# =========================================================================== +# OTHER USEFUL PARAMETERS FOR HUMAN PLAY ADJUSTMENT +# =========================================================================== + +# Choosing temperature near 1, and restricting it to only affect moves already below 1% chance, +# so that we sample close to the full range of human play. +# You can also reduce the temperature to settings more like the plain gtp_example.cfg. +# Then, rather than imitating a realistic human player, it will be more like imitating the +# *majority vote* of players at that rank. For example it would avoid a lot of blunders +# that players of that level would make, because even if players often blunder, the *majority vote* +# of players would be much less likely to select any given blunder that an individual player would. +chosenMoveTemperatureEarly = 0.85 +chosenMoveTemperature = 0.70 +chosenMoveTemperatureHalflife = 80 +chosenMoveTemperatureOnlyBelowProb = 0.01 # temperature only starts to dampen moves below this +chosenMoveSubtract = 0 +chosenMovePrune = 0 + +# Use a small NN cache to save memory since we're using very low visits anyways. You can increase +# these back to more like the plain gtp_example.cfg if you are doing more extensive searches to +# improve performance. +nnCacheSizePowerOfTwo = 17 +nnMutexPoolSizePowerOfTwo = 14 + +# =========================================================================== +# PARAMETERS CHANGED FROM DEFAULT TO MAKE SURE HUMAN SL USAGE WORKS WELL +# =========================================================================== + +# Make sure to take into account the recent moves in the game, don't ignore history. +# This will produce the best imitation/prediction, since humans definitely do play differently based on where the +# most recent moves in the game were, rather than coming fresh to the board position on every turn. +ignorePreRootHistory = false +analysisIgnorePreRootHistory = false + +# Average 2 neural net samples at the root - ensures a bit smoother probabilities and results in +# 8 * 7 / 2 = 28 possible policies instead of 8 possibilities. +rootNumSymmetriesToSample = 2 +# LCB improves strength for KataGo, but we disable it so it doesn't mess up move selection when blending human play. +useLcbForSelection = false + +# We disable dynamicScoreUtilityFactor - the human SL model can make score predictions that are a bit swingy, so +# if we do want to do a search that blends human SL values in (TODO there isn't a way to do this anyways yet), using +# static score utility might be a bit more stable. +winLossUtilityFactor = 1.0 +staticScoreUtilityFactor = 0.30 +dynamicScoreUtilityFactor = 0.00 + +# Uncertainty improves strength for KataGo normally, but messes with the weights of playouts in complicated ways, +# so lets turn it off when doing human SL stuff. +useUncertainty = false + +# Subtree value bias improves strength for KataGo normally, but messes with the values of nodes in complicated ways, +# so let's turn it off when doing human SL stuff. +subtreeValueBiasFactor = 0.0 + +# Noise pruning prunes out weight from moves that KataGo thinks are bad, but if we are doing human SL we might actively +# want to be playing or exploring and weighting "bad" but human-like moves. So disable this. +# Warning: when this is false, there is much less protection against the search severely misbehaving when you use too many threads. +# Make sure not to set numSearchThreads to be too large - at a minimum, keep at least a 20x buffer between +# the number of visits you use and the number of threads you use. +# (as an aside, ideally, you want to have visits be a sufficient factor larger than threads EVEN when +# useNoisePruning is true, this parameter just blunts the worst effects but doesn't entirely fix the badness). +useNoisePruning = false + +# CoreML settings-------------------------------------- + +# IF USING ONE MODEL: +numNNServerThreadsPerModel = 1 +coremlDeviceToUse = 0 # GPU +# coremlDeviceToUse = 100 # Neural Engine + +# IF USING TWO MODEL: Uncomment these three lines +# numNNServerThreadsPerModel = 2 +# coremlDeviceToUseThread0 = 0 # GPU +# coremlDeviceToUseThread1 = 100 # Neural Engine diff --git a/cpp/configs/misc/metal_gtp.cfg b/cpp/configs/misc/metal_gtp.cfg new file mode 100644 index 000000000..f27169535 --- /dev/null +++ b/cpp/configs/misc/metal_gtp.cfg @@ -0,0 +1,492 @@ +# Config for KataGo C++ GTP engine, i.e. "./katago.exe gtp" + +# RUNNING ON AN ONLINE SERVER OR IN A REAL TOURNAMENT OR MATCH: +# If you plan to do so, you may want to read through the "Rules" section +# below carefully for proper handling of komi and handicap games and end-of-game cleanup +# and various other details. + +# NOTES ABOUT PERFORMANCE AND MEMORY USAGE: +# You will likely want to tune one or more the following: +# +# numSearchThreads: +# The number of CPU threads to use. If your GPU is powerful, it can actually be much higher than +# the number of cores on your processor because you will need many threads to feed large enough +# batches to make good use of the GPU. +# +# The "./katago benchmark" command can help you tune this parameter, as well as to test out the effect +# of changes to any of the other parameters below! +# +# nnCacheSizePowerOfTwo: +# This controls the NN Cache size, which is the primary RAM/memory use. +# Increase this if you don't mind the memory use and want better performance for searches with +# tens of thousands of visits or more. Decrease this if you want to limit memory usage. +# +# If you're someone who is happy to do a bit of math - each neural net entry takes very +# approximately 1.5KB, except when using whole-board ownership/territory visualizations, each +# entry will take very approximately 3KB. The number of entries is (2 ** nnCacheSizePowerOfTwo), +# for example 2 ** 18 = 262144. +# +# OTHER NOTES: +# If you have more than one GPU, take a look at "OpenCL GPU settings" or "CUDA GPU settings" below. +# +# If using OpenCL, you will want to verify that KataGo is picking up the correct device! +# (e.g. some systems may have both an Intel CPU OpenCL and GPU OpenCL, if KataGo appears to pick +# the wrong one, you correct this by specifying "openclGpuToUse" below). +# +# You may also want to adjust "maxVisits", "ponderingEnabled", "resignThreshold", and possibly +# other parameters depending on your intended usage. +# +# ---------------------------------------------------------------------------------------- + +# For the `katago gtp` command, ALL of THE BELOW VALUES MAY BE SET OR OVERRIDDEN if desired via +# the command line arguments: +# -override-config KEY=VALUE,KEY=VALUE,... + +# Logs and files-------------------------------------------------------------------------- + +# Where to output log? +logDir = gtp_logs # Each run of KataGo will log to a separate file in this dir +# logDirDated = gtp_logs # Use this instead of logDir to also write separate dated subdirs +# logFile = gtp.log # Use this instead of logDir to just specify a single file directly + +# Logging options +logAllGTPCommunication = true +logSearchInfo = true +logToStderr = false + +# KataGo will display some info to stderr on GTP startup +# Uncomment this to suppress that and remain silent +# startupPrintMessageToStderr = false + +# Chat some stuff to stderr, for use in things like malkovich chat to OGS. +# ogsChatToStderr = true + +# Optionally override where KataGo will attempt to save things like openCLTuner files and other cached data. +# homeDataDir = DIRECTORY + +# Analysis------------------------------------------------------------------------------------ + +# Configure the maximum length of analysis printed out by lz-analyze and other places. +# Controls the number of moves after the first move in a variation. +# analysisPVLen = 15 + +# Report winrates for chat and analysis as (BLACK|WHITE|SIDETOMOVE). +# Default is SIDETOMOVE, which is what tools that use LZ probably also expect +# reportAnalysisWinratesAs = SIDETOMOVE + +# Larger values will make KataGo explore the top move(s) less deeply and accurately, +# but explore and give evaluations to a greater variety of moves, for analysis (does NOT affect play). +# Defaults to 0.04. +# An extreme value like 1 will distribute many playouts across every move on the board, even very bad moves. +# analysisWideRootNoise = 0.04 + + +# Default rules------------------------------------------------------------------------------------ +# See https://lightvector.github.io/KataGo/rules.html for a description of the rules. +# These rules are defaults and can be changed mid-run by several custom GTP commands. +# See https://github.com/lightvector/KataGo/blob/master/docs/GTP_Extensions.md for those commands. + +# Some other legal values are: "chinese", "japanese", "korean", "aga", "chinese-ogs", "new-zealand". +# KataGo does not claim to exactly match any particular human ruleset, but KataGo will try to behave +# as closely as possible given the rules it has implemented. +rules = tromp-taylor + +# Use the below instead to specify an arbitrary combination of individual rules. + +# koRule = SIMPLE # Simple ko rules (triple ko = no result) +# koRule = POSITIONAL # Positional superko +# koRule = SITUATIONAL # Situational superko + +# scoringRule = AREA # Area scoring +# scoringRule = TERRITORY # Territory scoring (uses a sort of special computer-friendly territory ruleset) + +# taxRule = NONE # All surrounded empty points are scored +# taxRule = SEKI # Eyes in seki do NOT count as points +# taxRule = ALL # All groups are taxed up to 2 points for the two eyes needed to live + +# multiStoneSuicideLegal = true # Is multiple-stone suicide legal? (Single-stone suicide is always illegal). + +# hasButton = false # Set to true when area scoring to award 0.5 points to the first pass. + +# friendlyPassOk = true # Set to true except for computer rulesets that requires capturing all stones before passing. + +# whiteHandicapBonus = 0 # In handicap games, give white no compensation for black's handicap stones (Tromp-taylor, NZ, JP) +# whiteHandicapBonus = N-1 # In handicap games, give white N-1 points for black's handicap stones (AGA) +# whiteHandicapBonus = N # In handicap games, give white N points for black's handicap stones (Chinese) + +# Uncomment and change to adjust what board size KataGo uses upon startup by default if GTP doesn't specify. +# defaultBoardSize = 19 +# Specify this to force a particular komi, EVEN if the GUI or GTP controller tries to set a different one +# ignoreGTPAndForceKomi = 7 + +# Bot behavior--------------------------------------------------------------------------------------- + +# Resignation ------------- + +# Resignation occurs if for at least resignConsecTurns in a row, +# the winLossUtility (which is on a [-1,1] scale) is below resignThreshold. +allowResignation = true +resignThreshold = -0.99 +resignConsecTurns = 6 +# Uncomment to make katago not resign close games, behind by fewer than this many points +# resignMinScoreDifference = 10 + +# Handicap ------------- + +# Assume that if black makes many moves in a row right at the start of the game, then the game is a handicap game. +# This is necessary on some servers and for some GUIs and also when initializing from many SGF files, which may +# set up a handicap game using repeated GTP "play" commands for black rather than GTP "place_free_handicap" commands. +# However, it may also lead to incorrect understanding of komi if whiteHandicapBonus is used and a server does NOT +# have such a practice. +# Defaults to true! Uncomment and set to false to disable this behavior. +# assumeMultipleStartingBlackMovesAreHandicap = true + +# Makes katago dynamically adjust in handicap or altered-komi games to assume based on those game settings that it +# must be stronger or weaker than the opponent and to play accordingly. Greatly improves handicap +# strength by biasing winrates and scores to favor appropriate safe/aggressive play. +# Does NOT affect analysis (lz-analyze, kata-analyze, used by programs like Lizzie) so analysis remains unbiased. +# Uncomment and set this to 0 to disable this and make KataGo play the same always. +# dynamicPlayoutDoublingAdvantageCapPerOppLead = 0.045 + +# Instead of a dynamic level, you can uncomment this and set this to a value from -3.0 to 3.0 to set KataGo's aggression to a FIXED level. +# DOES affect analysis tools (lz-analyze, kata-analyze, used by programs like Lizzie). +# Negative makes KataGo behave as if it is much weaker than the opponent, preferring to play defensively. +# Positive makes KataGo behave as if it is much stronger than the opponent, prefering to play aggressively or even overplay slightly. +# If this and "dynamicPlayoutDoublingAdvantageCapPerOppLead" are BOTH set then dynamic will be used for all games and this fixed +# value will be used for analysis tools. +# playoutDoublingAdvantage = 0.0 + +# Uncommenting one of these will enforce that the FIXED playoutDoublingAdvantage will only apply when KataGo plays the specified color +# and will be negated when playing the opposite color. +# playoutDoublingAdvantagePla = BLACK +# playoutDoublingAdvantagePla = WHITE + +# Passing and cleanup ------------- + +# Make the bot never assume that its pass will end the game, even if passing would end and "win" under Tromp-Taylor rules. +# Usually this is a good idea when using it for analysis or playing on servers where scoring may be implemented non-tromp-taylorly. +# Defaults to true! Uncomment and set to false to disable this. +# conservativePass = true + +# When using territory scoring, self-play games continue beyond two passes with special cleanup +# rules that may be confusing for human players. This option prevents the special cleanup phases from being +# reachable when using the bot for GTP play. +# Defaults to true! Uncomment and set to false if you want KataGo to be able to enter special cleanup. +# For example, if you are testing it against itself, or against another bot that has precisely implemented the rules +# documented at https://lightvector.github.io/KataGo/rules.html +# preventCleanupPhase = true + +# Misc Behavior -------------------- + +# If the board is symmetric, search only one copy of each equivalent move. Attempts to also account for ko/superko, will not theoretically perfect for superko. +# Uncomment and set to false to disable this. +# rootSymmetryPruning = true + +# Uncomment and set to true to make KataGo avoid a particular joseki that some KataGo nets misevaluate, +# and also to improve opening diversity versus some particular other bots that like to play it all the time. +# avoidMYTDaggerHack = false + +# Have KataGo mildly prefer to avoid playing the same joseki in every corner of the board. +# Uncomment to set to a specific value. Otherwise, defaults to 0 in even games, and to 0.005 in handicap games. +# See also the Avoid SGF mechanism at the bottom of this config. +# avoidRepeatedPatternUtility = 0.0 + +# Experimental logic to make KataGo fight a bit against mirror Go even with unfavorable komi. +# Enabled by default for GTP play, disabled for GTP analysis (i.e lizzie) and analysis engine. +# Uncomment and set to true to enable it for analysis, or false to disable it fully. +# antiMirror = true + +# Search limits----------------------------------------------------------------------------------- + +# For all of "maxVisits", "maxPlayouts", "maxTime", search will still try to follow GTP time controls and may make a move +# faster than the specified max if GTP tells it that it is playing under a clock as well in the current game. + +# If provided, limit maximum number of root visits per search to this much. (With tree reuse, visits do count earlier search) +maxVisits = 500 +# If provided, limit maximum number of new playouts per search to this much. (With tree reuse, playouts do not count earlier search) +# maxPlayouts = 300 +# If provided, cap search time at this many seconds. +# maxTime = 10 + +# Ponder on the opponent's turn? +ponderingEnabled = false +maxTimePondering = 60 # Maximum time to ponder, in seconds. Comment out to make unlimited. +# Note: you can set "maxVisitsPondering" or "maxPlayoutsPondering" too. + +# Approx number of seconds to buffer for lag for GTP time controls - will move a bit faster assuming there is this much lag per move. +lagBuffer = 1.0 + +# Number of threads to use in search +numSearchThreads = 32 + +# Play a little faster if the opponent is passing, for friendliness +searchFactorAfterOnePass = 0.50 +searchFactorAfterTwoPass = 0.25 +# Play a little faster if super-winning, for friendliness +searchFactorWhenWinning = 0.40 +searchFactorWhenWinningThreshold = 0.95 + +# GPU Settings------------------------------------------------------------------------------- + +# Maximum number of positions to send to a single GPU at once. +# The default value here is roughly equal to numSearchThreads, but you can specify it manually +# if you are running out of memory, or if you are using multiple GPUs that expect to split +# up the work. +nnMaxBatchSize = 16 + +# Cache up to (2 ** this) many neural net evaluations in case of transpositions in the tree. +# Uncomment and edit to change if you want to adjust a major component of KataGo's RAM usage. +# nnCacheSizePowerOfTwo = 20 + +# Size of mutex pool for nnCache is (2 ** this). +# nnMutexPoolSizePowerOfTwo = 16 + +# Randomize board orientation when running neural net evals? Uncomment and set to false to disable. +# nnRandomize = true +# If provided, force usage of a specific seed for nnRandomize instead of randomizing. +# nnRandSeed = abcdefg + +# TO USE MULTIPLE GPUS: +# Metal + CoreML backends hack here. +# Metal backend runs the default GPU 0. +# CoreML backend runs at the other thread. +# So, if you want to use Metal + CoreML, you should set numNNServerThreadsPerModel to 2. +numNNServerThreadsPerModel = 1 + + +# TENSORRT GPU settings-------------------------------------- +# These only apply when using the TENSORRT version of KataGo. + +# IF USING ONE GPU: optionally uncomment and change this if the GPU you want to use turns out to be not device 0 +# trtDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines (AND set numNNServerThreadsPerModel above): +# trtDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# trtDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 + +# IF USING THREE GPUS: Uncomment these three lines (AND set numNNServerThreadsPerModel above): +# trtDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# trtDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 +# trtDeviceToUseThread2 = 2 # change this if the third GPU you want to use turns out to be not device 2 + +# You can probably guess the pattern if you have four, five, etc. GPUs. + + +# CUDA GPU settings-------------------------------------- +# These only apply when using the CUDA version of KataGo. + +# IF USING ONE GPU: optionally uncomment and change this if the GPU you want to use turns out to be not device 0 +# cudaDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines (AND set numNNServerThreadsPerModel above): +# cudaDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# cudaDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 + +# IF USING THREE GPUS: Uncomment these three lines (AND set numNNServerThreadsPerModel above): +# cudaDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# cudaDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 +# cudaDeviceToUseThread2 = 2 # change this if the third GPU you want to use turns out to be not device 2 + +# You can probably guess the pattern if you have four, five, etc. GPUs. + +# KataGo will automatically use FP16 or not based on the compute capability of your NVIDIA GPU. If you +# want to try to force a particular behavior though you can uncomment these lines and change them +# to "true" or "false". E.g. it's using FP16 but on your card that's giving an error, or it's not using +# FP16 but you think it should. +# cudaUseFP16 = auto +# cudaUseNHWC = auto + + +# OpenCL GPU settings-------------------------------------- +# These only apply when using the OpenCL version of KataGo. + +# Uncomment to tune OpenCL for every board size separately, rather than only the largest possible size +# openclReTunePerBoardSize = true + +# IF USING ONE GPU: optionally uncomment and change this if the best device to use is guessed incorrectly. +# The default behavior tries to guess the 'best' GPU or device on your system to use, usually it will be a good guess. +# openclDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines and replace X and Y with the device ids of the devices you want to use. +# It might NOT be 0 and 1, some computers will have many OpenCL devices. You can see what the devices are when +# KataGo starts up - it should print or log all the devices it finds. +# (AND also set numNNServerThreadsPerModel above) +# openclDeviceToUseThread0 = X +# openclDeviceToUseThread1 = Y + +# IF USING THREE GPUS: Uncomment these three lines and replace X and Y and Z with the device ids of the devices you want to use. +# It might NOT be 0 and 1 and 2, some computers will have many OpenCL devices. You can see what the devices are when +# KataGo starts up - it should print or log all the devices it finds. +# (AND also set numNNServerThreadsPerModel above) +# openclDeviceToUseThread0 = X +# openclDeviceToUseThread1 = Y +# openclDeviceToUseThread2 = Z + +# You can probably guess the pattern if you have four, five, etc. GPUs. + +# KataGo will automatically use FP16 or not based on testing your GPU during tuning. If you +# want to try to force a particular behavior though you can uncomment this lines and change it +# to "true" or "false". This is a fairly blunt setting - more detailed settings are testable +# by rerunning the tuner with various arguments. +# openclUseFP16 = auto + + +# Eigen-specific settings-------------------------------------- +# These only apply when using the Eigen (pure CPU) version of KataGo. + +# This is the number of CPU threads for evaluating the neural net on the Eigen backend. +# It defaults to numSearchThreads. +# numEigenThreadsPerModel = X + +# CoreML settings-------------------------------------- +# These only apply when using the CoreML version of KataGo. + +# IF USING ONE MODEL: +coremlDeviceToUse = 0 # GPU + +# IF USING TWO MODEL: Uncomment these two lines +# (AND also set numNNServerThreadsPerModel = 2 above) +# coremlDeviceToUseThread0 = 0 # GPU +# coremlDeviceToUseThread1 = 100 # Neural Engine + +# IF USING THREE MODEL: Uncomment these three lines +# (AND also set numNNServerThreadsPerModel = 3 above) +# coremlDeviceToUseThread0 = 0 # GPU +# coremlDeviceToUseThread1 = 100 # Neural Engine +# coremlDeviceToUseThread2 = 101 # Neural Engine + +# If you want to force the backend using float-point 16-bit or 32-bit, you can uncomment +# this lines and change it to "true" or "false". +# coremlUseFP16 = auto + +# You can probably guess the pattern if you have four, five, etc. Models. + +# Root move selection and biases------------------------------------------------------------------------------ +# Uncomment and edit any of the below values to change them from their default. + +# If provided, force usage of a specific seed for various things in the search instead of randomizing +# searchRandSeed = hijklmn + +# Temperature for the early game, randomize between chosen moves with this temperature +# chosenMoveTemperatureEarly = 0.5 +# Decay temperature for the early game by 0.5 every this many moves, scaled with board size. +# chosenMoveTemperatureHalflife = 19 +# At the end of search after the early game, randomize between chosen moves with this temperature +# chosenMoveTemperature = 0.10 +# Subtract this many visits from each move prior to applying chosenMoveTemperature +# (unless all moves have too few visits) to downweight unlikely moves +# chosenMoveSubtract = 0 +# The same as chosenMoveSubtract but only prunes moves that fall below the threshold, does not affect moves above +# chosenMovePrune = 1 + +# Number of symmetries to sample (WITHOUT replacement) and average at the root +# rootNumSymmetriesToSample = 1 + +# Using LCB for move selection? +# useLcbForSelection = true +# How many stdevs a move needs to be better than another for LCB selection +# lcbStdevs = 5.0 +# Only use LCB override when a move has this proportion of visits as the top move +# minVisitPropForLCB = 0.15 + +# Internal params------------------------------------------------------------------------------ +# Uncomment and edit any of the below values to change them from their default. + +# Scales the utility of winning/losing +# winLossUtilityFactor = 1.0 +# Scales the utility for trying to maximize score +# staticScoreUtilityFactor = 0.10 +# dynamicScoreUtilityFactor = 0.30 +# Adjust dynamic score center this proportion of the way towards zero, capped at a reasonable amount. +# dynamicScoreCenterZeroWeight = 0.20 +# dynamicScoreCenterScale = 0.75 +# The utility of getting a "no result" due to triple ko or other long cycle in non-superko rulesets (-1 to 1) +# noResultUtilityForWhite = 0.0 +# The number of wins that a draw counts as, for white. (0 to 1) +# drawEquivalentWinsForWhite = 0.5 + +# Exploration constant for mcts +# cpuctExploration = 1.0 +# cpuctExplorationLog = 0.45 + +# Parameters that control exploring more in volatile positions, exploring less in stable positions. +# cpuctUtilityStdevPrior = 0.40 +# cpuctUtilityStdevPriorWeight = 2.0 +# cpuctUtilityStdevScale = 0.85 + +# FPU reduction constant for mcts +# fpuReductionMax = 0.2 +# rootFpuReductionMax = 0.1 +# fpuParentWeightByVisitedPolicy = true + +# Parameters that control weighting of evals based on the net's own self-reported uncertainty. +# useUncertainty = true +# uncertaintyExponent = 1.0 +# uncertaintyCoeff = 0.25 + +# Amount to apply a downweighting of children with very bad values relative to good ones +# valueWeightExponent = 0.25 + +# Slight incentive for the bot to behave human-like with regard to passing at the end, filling the dame, +# not wasting time playing in its own territory, etc, and not play moves that are equivalent in terms of +# points but a bit more unfriendly to humans. +# rootEndingBonusPoints = 0.5 + +# Make the bot prune useless moves that are just prolonging the game to avoid losing yet +# rootPruneUselessMoves = true + +# Apply bias correction based on local pattern keys +# subtreeValueBiasFactor = 0.45 +# subtreeValueBiasWeightExponent = 0.85 + +# Use graph search rather than tree search - identify and share search for transpositions. +# useGraphSearch = true + +# How much to shard the node table for search synchronization +# nodeTableShardsPowerOfTwo = 16 +# How many virtual losses to add when a thread descends through a node +# numVirtualLossesPerThread = 1 + +# Improve the quality of evals under heavy multithreading +# useNoisePruning = true + + +# Avoid SGF Patterns ------------------------------------------------------------------------------ +# The parameters in this section provide a powerful way to customize KataGo to avoid moves that follow specific patterns +# based on a set of provided SGF files loaded upon startup. Uncomment them to use this feature. +# Additionally, if the SGF file contains the string %SKIP% in a comment on a move, that move will be ignored for this purpose. + +# Load sgf files from this directory when the engine is started (ONLY on startup, will not reload unless engine is restarted) +# avoidSgfPatternDirs = path/to/directory/with/sgfs/ + +# Penalize this much utility per matching move. +# Set this negative if you instead want to make KataGo favor the SGF patterns instead of penalizing it! +# This number does not need to be large, even 0.001 will make a difference. Too-large values may lead to bad play. +# avoidSgfPatternUtility = 0.001 + +# Optional - load only the newest this many files +# avoidSgfPatternMaxFiles = 20 + +# Optional - Penalty is multiplied by this per each older SGF file, so that old sgf files matter less than newer ones. +# avoidSgfPatternLambda = 0.90 + +# Optional - pay attention only to moves that were made by players with this name. +# For example you can set it to the name that your bot's past games will show up as in the SGF, so that the bot will only avoid repeating +# moves that itself made in past games, not the moves that its opponents made. +# avoidSgfPatternAllowedNames = my-ogs-bot-name1,my-ogs-bot-name2 + +# Optional - Ignore any moves in SGF files that occurred before this turn number. +# avoidSgfPatternMinTurnNumber = 0 + +# For more avoid patterns: +# You can also specify a second set of parameters, and a third, fourth, etc by numbering 2,3,4,... +# avoidSgf2PatternDirs = ... +# avoidSgf2PatternUtility = ... +# avoidSgf2PatternMaxFiles = ... +# avoidSgf2PatternLambda = ... +# avoidSgf2PatternAllowedNames = ... +# avoidSgf2PatternMinTurnNumber = ... + + + + diff --git a/cpp/configs/training/gatekeeper1.cfg b/cpp/configs/training/gatekeeper1.cfg index 03eb535eb..629521923 100644 --- a/cpp/configs/training/gatekeeper1.cfg +++ b/cpp/configs/training/gatekeeper1.cfg @@ -15,7 +15,7 @@ logToStdout = true # Match----------------------------------------------------------------------------------- -numGameThreads = 128 +numGameThreads = 16 maxMovesPerGame = 1600 numGamesPerGating = 200 @@ -51,21 +51,16 @@ numSearchThreads = 1 # GPU Settings------------------------------------------------------------------------------- -nnMaxBatchSize = 128 +nnMaxBatchSize = 8 nnCacheSizePowerOfTwo = 21 nnMutexPoolSizePowerOfTwo = 15 -numNNServerThreadsPerModel = 1 +numNNServerThreadsPerModel = 2 nnRandomize = true -# CUDA GPU settings-------------------------------------- -# cudaDeviceToUse = 0 #use device 0 for all server threads (numNNServerThreadsPerModel) unless otherwise specified per-model or per-thread-per-model -# cudaDeviceToUseModel0 = 3 #use device 3 for model 0 for all threads unless otherwise specified per-thread for this model -# cudaDeviceToUseModel1 = 2 #use device 2 for model 1 for all threads unless otherwise specified per-thread for this model -# cudaDeviceToUseModel0Thread0 = 3 #use device 3 for model 0, server thread 0 -# cudaDeviceToUseModel0Thread1 = 2 #use device 2 for model 0, server thread 1 - -cudaUseFP16 = auto -cudaUseNHWC = auto +# coremlDeviceToUseThread0 = 0 # GPU +# coremlDeviceToUseThread1 = 1 # GPU +coremlDeviceToUseThread0 = 100 # NPU +coremlDeviceToUseThread1 = 101 # NPU # Root move selection and biases------------------------------------------------------------------------------ diff --git a/cpp/configs/training/gatekeeper1_maxsize9.cfg b/cpp/configs/training/gatekeeper1_maxsize9.cfg index 6dbedc484..842062970 100644 --- a/cpp/configs/training/gatekeeper1_maxsize9.cfg +++ b/cpp/configs/training/gatekeeper1_maxsize9.cfg @@ -15,7 +15,7 @@ logToStdout = true # Match----------------------------------------------------------------------------------- -numGameThreads = 128 +numGameThreads = 16 maxMovesPerGame = 1600 numGamesPerGating = 200 @@ -51,21 +51,16 @@ numSearchThreads = 1 # GPU Settings------------------------------------------------------------------------------- -nnMaxBatchSize = 128 +nnMaxBatchSize = 8 nnCacheSizePowerOfTwo = 21 nnMutexPoolSizePowerOfTwo = 15 -numNNServerThreadsPerModel = 1 +numNNServerThreadsPerModel = 2 nnRandomize = true -# CUDA GPU settings-------------------------------------- -# cudaDeviceToUse = 0 #use device 0 for all server threads (numNNServerThreadsPerModel) unless otherwise specified per-model or per-thread-per-model -# cudaDeviceToUseModel0 = 3 #use device 3 for model 0 for all threads unless otherwise specified per-thread for this model -# cudaDeviceToUseModel1 = 2 #use device 2 for model 1 for all threads unless otherwise specified per-thread for this model -# cudaDeviceToUseModel0Thread0 = 3 #use device 3 for model 0, server thread 0 -# cudaDeviceToUseModel0Thread1 = 2 #use device 2 for model 0, server thread 1 - -cudaUseFP16 = auto -cudaUseNHWC = auto +# coremlDeviceToUseThread0 = 0 # GPU +# coremlDeviceToUseThread1 = 1 # GPU +coremlDeviceToUseThread0 = 100 # NPU +coremlDeviceToUseThread1 = 101 # NPU # Root move selection and biases------------------------------------------------------------------------------ diff --git a/cpp/configs/training/selfplay1.cfg b/cpp/configs/training/selfplay1.cfg index 100a60d9c..c157b82a2 100644 --- a/cpp/configs/training/selfplay1.cfg +++ b/cpp/configs/training/selfplay1.cfg @@ -81,7 +81,7 @@ fancyKomiVarying = true # In non-compensated handicap and fork games, vary komi # Match----------------------------------------------------------------------------------- -numGameThreads = 128 +numGameThreads = 16 maxMovesPerGame = 1600 # Rules------------------------------------------------------------------------------------ @@ -117,21 +117,16 @@ numSearchThreads = 1 # GPU Settings------------------------------------------------------------------------------- -nnMaxBatchSize = 128 +nnMaxBatchSize = 8 nnCacheSizePowerOfTwo = 21 nnMutexPoolSizePowerOfTwo = 15 -numNNServerThreadsPerModel = 1 +numNNServerThreadsPerModel = 2 nnRandomize = true -# CUDA GPU settings-------------------------------------- -# cudaDeviceToUse = 0 #use device 0 for all server threads (numNNServerThreadsPerModel) unless otherwise specified per-model or per-thread-per-model -# cudaDeviceToUseModel0 = 3 #use device 3 for model 0 for all threads unless otherwise specified per-thread for this model -# cudaDeviceToUseModel1 = 2 #use device 2 for model 1 for all threads unless otherwise specified per-thread for this model -# cudaDeviceToUseModel0Thread0 = 3 #use device 3 for model 0, server thread 0 -# cudaDeviceToUseModel0Thread1 = 2 #use device 2 for model 0, server thread 1 - -cudaUseFP16 = auto -cudaUseNHWC = auto +# coremlDeviceToUseThread0 = 0 # GPU +# coremlDeviceToUseThread1 = 1 # GPU +coremlDeviceToUseThread0 = 100 # NPU +coremlDeviceToUseThread1 = 101 # NPU # Root move selection and biases------------------------------------------------------------------------------ diff --git a/cpp/configs/training/selfplay1_maxsize9.cfg b/cpp/configs/training/selfplay1_maxsize9.cfg index 0446706bd..7d4b755d5 100644 --- a/cpp/configs/training/selfplay1_maxsize9.cfg +++ b/cpp/configs/training/selfplay1_maxsize9.cfg @@ -81,7 +81,7 @@ fancyKomiVarying = true # In non-compensated handicap and fork games, vary komi # Match----------------------------------------------------------------------------------- -numGameThreads = 128 +numGameThreads = 16 maxMovesPerGame = 1600 # Rules------------------------------------------------------------------------------------ @@ -117,21 +117,16 @@ numSearchThreads = 1 # GPU Settings------------------------------------------------------------------------------- -nnMaxBatchSize = 128 +nnMaxBatchSize = 8 nnCacheSizePowerOfTwo = 21 nnMutexPoolSizePowerOfTwo = 15 -numNNServerThreadsPerModel = 1 +numNNServerThreadsPerModel = 2 nnRandomize = true -# CUDA GPU settings-------------------------------------- -# cudaDeviceToUse = 0 #use device 0 for all server threads (numNNServerThreadsPerModel) unless otherwise specified per-model or per-thread-per-model -# cudaDeviceToUseModel0 = 3 #use device 3 for model 0 for all threads unless otherwise specified per-thread for this model -# cudaDeviceToUseModel1 = 2 #use device 2 for model 1 for all threads unless otherwise specified per-thread for this model -# cudaDeviceToUseModel0Thread0 = 3 #use device 3 for model 0, server thread 0 -# cudaDeviceToUseModel0Thread1 = 2 #use device 2 for model 0, server thread 1 - -cudaUseFP16 = auto -cudaUseNHWC = auto +# coremlDeviceToUseThread0 = 0 # GPU +# coremlDeviceToUseThread1 = 1 # GPU +coremlDeviceToUseThread0 = 100 # NPU +coremlDeviceToUseThread1 = 101 # NPU # Root move selection and biases------------------------------------------------------------------------------ diff --git a/cpp/dataio/loadmodel.cpp b/cpp/dataio/loadmodel.cpp index 81483b170..673134af0 100644 --- a/cpp/dataio/loadmodel.cpp +++ b/cpp/dataio/loadmodel.cpp @@ -19,7 +19,6 @@ std::time_t to_time_t(TP tp) static const vector ACCEPTABLE_MODEL_SUFFIXES { ".bin.gz", - ".bin", "model.txt.gz", "model.txt" }; diff --git a/cpp/macos/cmake/modules/AddSwift.cmake b/cpp/macos/cmake/modules/AddSwift.cmake new file mode 100644 index 000000000..3860be451 --- /dev/null +++ b/cpp/macos/cmake/modules/AddSwift.cmake @@ -0,0 +1,50 @@ +# This source file is part of the Swift open source project +# +# Copyright (c) 2023 Apple Inc. and the Swift project authors. +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information + +include(CheckCompilerFlag) + +# Generate bridging header from Swift to C++ +# NOTE: This logic will eventually be upstreamed into CMake +function(_swift_generate_cxx_header_target target module header) + cmake_parse_arguments(ARG "" "" "SOURCES;SEARCH_PATHS;DEPENDS" ${ARGN}) + if(NOT ARG_SOURCES) + message(FATAL_ERROR "No sources provided to 'swift_generate_cxx_header_target'") + endif() + + if(ARG_SEARCH_PATHS) + list(TRANSFORM ARG_SEARCH_PATHS PREPEND "-I") + string(REPLACE ";" " " EXPANDED_SEARCH_PATHS "${ARG_SEARCH_PATHS}") + endif() + + if(APPLE) + set(SDK_FLAGS "-sdk" "${CMAKE_OSX_SYSROOT}") + elseif(WIN32) + set(SDK_FLAGS "-sdk" "$ENV{SDKROOT}") + endif() + + add_custom_command( + OUTPUT + "${header}" + COMMAND + ${CMAKE_Swift_COMPILER} -frontend -typecheck + ${EXPANDED_SEARCH_PATHS} + ${ARG_SOURCES} + ${SDK_FLAGS} + -module-name "${module}" + -cxx-interoperability-mode=default + -emit-clang-header-path "${header}" + DEPENDS + ${ARG_DEPENDS} + COMMENT + "Generating '${header}'" + ) + + add_custom_target("${target}" + DEPENDS + "${header}" + ) +endfunction() diff --git a/cpp/macos/cmake/modules/InitializeSwift.cmake b/cpp/macos/cmake/modules/InitializeSwift.cmake new file mode 100644 index 000000000..b3f43904b --- /dev/null +++ b/cpp/macos/cmake/modules/InitializeSwift.cmake @@ -0,0 +1,89 @@ +# This source file is part of the Swift open source project +# +# Copyright (c) 2023 Apple Inc. and the Swift project authors. +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information + +# Compute the name of the architecture directory on Windows from the CMake +# system processor name. +function(_swift_windows_arch_name output_variable_name target_arch) + if(NOT WIN32) + return() + endif() + + if("${target_arch}" STREQUAL "AMD64") + set("${output_variable_name}" "x86_64" PARENT_SCOPE) + elseif("${target_arch}" STREQUAL "ARM64") + set("${output_variable_name}" "aarch64" PARENT_SCOPE) + else() + message(FATAL_ERROR "Unknown windows architecture: ${target_arch}") + endif() +endfunction() + +# Compute flags and search paths +# NOTE: This logic will eventually move to CMake +function(_setup_swift_paths) + # If we haven't set the swift library search paths, do that now + if(NOT SWIFT_LIBRARY_SEARCH_PATHS) + if(APPLE) + set(SDK_FLAGS "-sdk" "${CMAKE_OSX_SYSROOT}") + endif() + + # Note: This does not handle cross-compiling correctly. + # To handle it correctly, we would need to pass the target triple and + # flags to this compiler invocation. + execute_process( + COMMAND ${CMAKE_Swift_COMPILER} ${SDK_FLAGS} -print-target-info + OUTPUT_VARIABLE SWIFT_TARGET_INFO + ) + + # extract search paths from swift driver response + string(JSON SWIFT_TARGET_PATHS GET ${SWIFT_TARGET_INFO} "paths") + + string(JSON SWIFT_TARGET_LIBRARY_PATHS GET ${SWIFT_TARGET_PATHS} "runtimeLibraryPaths") + string(JSON SWIFT_TARGET_LIBRARY_PATHS_LENGTH LENGTH ${SWIFT_TARGET_LIBRARY_PATHS}) + math(EXPR SWIFT_TARGET_LIBRARY_PATHS_LENGTH "${SWIFT_TARGET_LIBRARY_PATHS_LENGTH} - 1 ") + + string(JSON SWIFT_TARGET_LIBRARY_IMPORT_PATHS GET ${SWIFT_TARGET_PATHS} "runtimeLibraryImportPaths") + string(JSON SWIFT_TARGET_LIBRARY_IMPORT_PATHS_LENGTH LENGTH ${SWIFT_TARGET_LIBRARY_IMPORT_PATHS}) + math(EXPR SWIFT_TARGET_LIBRARY_IMPORT_PATHS_LENGTH "${SWIFT_TARGET_LIBRARY_IMPORT_PATHS_LENGTH} - 1 ") + + string(JSON SWIFT_SDK_IMPORT_PATH ERROR_VARIABLE errno GET ${SWIFT_TARGET_PATHS} "sdkPath") + + foreach(JSON_ARG_IDX RANGE ${SWIFT_TARGET_LIBRARY_PATHS_LENGTH}) + string(JSON SWIFT_LIB GET ${SWIFT_TARGET_LIBRARY_PATHS} ${JSON_ARG_IDX}) + list(APPEND SWIFT_SEARCH_PATHS ${SWIFT_LIB}) + endforeach() + + foreach(JSON_ARG_IDX RANGE ${SWIFT_TARGET_LIBRARY_IMPORT_PATHS_LENGTH}) + string(JSON SWIFT_LIB GET ${SWIFT_TARGET_LIBRARY_IMPORT_PATHS} ${JSON_ARG_IDX}) + list(APPEND SWIFT_SEARCH_PATHS ${SWIFT_LIB}) + endforeach() + + if(SWIFT_SDK_IMPORT_PATH) + list(APPEND SWIFT_SEARCH_PATHS ${SWIFT_SDK_IMPORT_PATH}) + endif() + + # Save the swift library search paths + set(SWIFT_LIBRARY_SEARCH_PATHS ${SWIFT_SEARCH_PATHS} CACHE FILEPATH "Swift driver search paths") + endif() + + link_directories(${SWIFT_LIBRARY_SEARCH_PATHS}) + + if(WIN32) + _swift_windows_arch_name(SWIFT_WIN_ARCH_DIR "${CMAKE_SYSTEM_PROCESSOR}") + set(SWIFT_SWIFTRT_FILE "$ENV{SDKROOT}/usr/lib/swift/windows/${SWIFT_WIN_ARCH_DIR}/swiftrt.obj") + add_link_options("$<$:${SWIFT_SWIFTRT_FILE}>") + elseif(NOT APPLE) + find_file(SWIFT_SWIFTRT_FILE + swiftrt.o + PATHS ${SWIFT_LIBRARY_SEARCH_PATHS} + NO_CACHE + REQUIRED + NO_DEFAULT_PATH) + add_link_options("$<$:${SWIFT_SWIFTRT_FILE}>") + endif() +endfunction() + +_setup_swift_paths() diff --git a/cpp/main.cpp b/cpp/main.cpp index f86a44a27..f46d2e72c 100644 --- a/cpp/main.cpp +++ b/cpp/main.cpp @@ -15,6 +15,7 @@ #include "core/using.h" //------------------------ +#ifndef OS_IS_IOS static void printHelp(const vector& args) { cout << endl; if(args.size() >= 1) @@ -211,6 +212,7 @@ int main(int argc, const char* const* argv) { return handleSubcommand(cmdArg, args); #endif } +#endif string Version::getKataGoVersion() { @@ -241,6 +243,8 @@ string Version::getKataGoVersionFullInfo() { out << "Using OpenCL backend" << endl; #elif defined(USE_EIGEN_BACKEND) out << "Using Eigen(CPU) backend" << endl; +#elif defined(USE_COREML_BACKEND) + out << "Using CoreML backend" << endl; #else out << "Using dummy backend" << endl; #endif @@ -277,6 +281,8 @@ string Version::getGitRevisionWithBackend() { s += "-opencl"; #elif defined(USE_EIGEN_BACKEND) s += "-eigen"; +#elif defined(USE_COREML_BACKEND) + s += "-coreml"; #else s += "-dummy"; #endif diff --git a/cpp/neuralnet/coremlbackend.cpp b/cpp/neuralnet/coremlbackend.cpp new file mode 100644 index 000000000..401f42a2a --- /dev/null +++ b/cpp/neuralnet/coremlbackend.cpp @@ -0,0 +1,257 @@ +#ifdef USE_COREML_BACKEND + +#include "../neuralnet/modelversion.h" +#include "../neuralnet/nneval.h" +#include "../neuralnet/nninputs.h" +#include "../neuralnet/nninterface.h" +#include "../neuralnet/metalbackend.h" +#include "../neuralnet/coremlbackend.h" + + +using namespace std; + +//-------------------------------------------------------------- + +size_t CoreMLProcess::calculateBufferOffset(size_t row, size_t singleResultElts, size_t resultChannels) { + return row * singleResultElts * resultChannels; +} + +int CoreMLProcess::calculateIndex(const int y, const int x, const int xLen) { + return (y * xLen) + x; +} + +float CoreMLProcess::policyOptimismCalc(const double policyOptimism, const float p, const float pOpt) { + return MetalProcess::policyOptimismCalc(policyOptimism, p, pOpt); +} + +float CoreMLProcess::assignPolicyValue( + const size_t modelPolicyResultChannels, + const double policyOptimism, + const float* targetBuffer, + const size_t outputIdx, + const size_t singleModelPolicyResultElts) { + const size_t pOptIndex = 5; + return (modelPolicyResultChannels == 1) + ? targetBuffer[outputIdx] + : policyOptimismCalc( + policyOptimism, targetBuffer[outputIdx], targetBuffer[outputIdx + (pOptIndex * singleModelPolicyResultElts)]); +} + +void CoreMLProcess::processPolicy( + InputBuffers* inputBuffers, + NNOutput* currentOutput, + const ComputeHandle* gpuHandle, + NNResultBuf* inputBuf, + size_t row) { + const int gpuHandleXLen = gpuHandle->nnXLen; + const int gpuHandleYLen = gpuHandle->nnYLen; + const int modelXLen = gpuHandle->modelXLen; + const int modelYLen = gpuHandle->modelYLen; + const int singleModelPolicyResultElts = (modelXLen * modelYLen) + 1; + auto& inputBuffersRef = *inputBuffers; + const size_t targetBufferOffset = + calculateBufferOffset(row, singleModelPolicyResultElts, inputBuffersRef.modelPolicyResultChannels); + const size_t currentBufferOffset = + calculateBufferOffset(row, inputBuffersRef.singlePolicyProbsElts, inputBuffersRef.policyResultChannels); + float* targetBuffer = &inputBuffersRef.modelPolicyResults[targetBufferOffset]; + float* currentBuffer = &inputBuffersRef.policyProbsBuffer[currentBufferOffset]; + const auto symmetry = inputBuf->symmetry; + const auto policyOptimism = inputBuf->policyOptimism; + + auto processBuffer = [&](int y, int x) { + int outputIdx = calculateIndex(y, x, modelXLen); + int probsIdx = calculateIndex(y, x, gpuHandleXLen); + + currentBuffer[probsIdx] = assignPolicyValue( + inputBuffersRef.modelPolicyResultChannels, + policyOptimism, + targetBuffer, + outputIdx, + singleModelPolicyResultElts); + }; + + for(int y = 0; y < gpuHandleYLen; y++) { + for(int x = 0; x < gpuHandleXLen; x++) { + processBuffer(y, x); + } + } + + assert(singleModelPolicyResultElts > 0); + assert(inputBuffersRef.singlePolicyProbsElts > 0); + size_t endOfModelPolicyIdx = singleModelPolicyResultElts - 1; + size_t endOfPolicyProbsIdx = inputBuffersRef.singlePolicyProbsElts - 1; + + currentOutput->policyProbs[endOfPolicyProbsIdx] = assignPolicyValue( + inputBuffersRef.modelPolicyResultChannels, + policyOptimism, + targetBuffer, + endOfModelPolicyIdx, + singleModelPolicyResultElts); + + SymmetryHelpers::copyOutputsWithSymmetry( + currentBuffer, currentOutput->policyProbs, 1, gpuHandleYLen, gpuHandleXLen, symmetry); +} + +void CoreMLProcess::processValue( + const InputBuffers* inputBuffers, + NNOutput* currentOutput, + const size_t row) { + MetalProcess::processValue(inputBuffers, currentOutput, row); +} + +void CoreMLProcess::processOwnership( + const InputBuffers* inputBuffers, + NNOutput* currentOutput, + const ComputeHandle* gpuHandle, + const int symmetry, + const size_t row) { + // If there's no ownership map, we have nothing to do + if(currentOutput->whiteOwnerMap == nullptr) { + return; + } + + // Extract useful values from buffers and GPU handle + const int nnXLen = gpuHandle->nnXLen; + const int nnYLen = gpuHandle->nnYLen; + const int modelXLen = gpuHandle->modelXLen; + const int modelYLen = gpuHandle->modelYLen; + + // CoreML model and NN ownership result elements differ + const size_t singleOwnershipResultElts = modelXLen * modelYLen; + const size_t singleOwnerMapElts = inputBuffers->singleOwnerMapElts; + + // Calculate starting points in the buffers + const float* ownershipOutputBuf = &inputBuffers->ownershipResults[row * singleOwnershipResultElts]; + float* ownerMapBuf = &inputBuffers->ownerMapBuffer[row * singleOwnerMapElts]; + + // Copy data from ownership output buffer to owner map buffer + for(int y = 0; y < nnYLen; y++) { + for(int x = 0; x < nnXLen; x++) { + int outputIdx = calculateIndex(y, x, modelXLen); + int ownerMapIdx = calculateIndex(y, x, nnXLen); + ownerMapBuf[ownerMapIdx] = ownershipOutputBuf[outputIdx]; + } + } + + // Apply symmetry to the owner map buffer and copy it to the output's whiteOwnerMap + SymmetryHelpers::copyOutputsWithSymmetry(ownerMapBuf, currentOutput->whiteOwnerMap, 1, nnYLen, nnXLen, symmetry); +} + +void CoreMLProcess::processScoreValues( + const InputBuffers* inputBuffers, + NNOutput* currentOutput, + const int version, + const size_t row) { + const size_t singleScoreValuesResultElts = inputBuffers->singleScoreValuesResultElts; + const size_t scoreValuesOutputBufOffset = row * singleScoreValuesResultElts; + const float* scoreValuesOutputBuf = &inputBuffers->scoreValuesResults[scoreValuesOutputBufOffset]; + const size_t singleMoreMiscValuesResultElts = inputBuffers->singleMoreMiscValuesResultElts; + const size_t moreMiscValuesOutputBufOffset = row * singleMoreMiscValuesResultElts; + const float* moreMiscValuesOutputBuf = &inputBuffers->moreMiscValuesResults[moreMiscValuesOutputBufOffset]; + + currentOutput->whiteScoreMean = scoreValuesOutputBuf[0]; + currentOutput->whiteScoreMeanSq = currentOutput->whiteScoreMean * currentOutput->whiteScoreMean; + currentOutput->whiteLead = currentOutput->whiteScoreMean; + currentOutput->varTimeLeft = 0.0f; + currentOutput->shorttermWinlossError = 0.0f; + currentOutput->shorttermScoreError = 0.0f; + + if(version >= 4) { + currentOutput->whiteScoreMean = scoreValuesOutputBuf[0]; + currentOutput->whiteScoreMeanSq = scoreValuesOutputBuf[1]; + currentOutput->whiteLead = (version >= 8) ? scoreValuesOutputBuf[2] : currentOutput->whiteScoreMean; + currentOutput->varTimeLeft = (version >= 9) ? scoreValuesOutputBuf[3] : currentOutput->varTimeLeft; + currentOutput->shorttermWinlossError = + (version >= 9) ? moreMiscValuesOutputBuf[0] : currentOutput->shorttermWinlossError; + currentOutput->shorttermScoreError = (version >= 9) ? moreMiscValuesOutputBuf[1] : currentOutput->shorttermScoreError; + } +} + +void CoreMLProcess::getCoreMLOutput( + ComputeHandle* gpuHandle, + InputBuffers* inputBuffers, + int numBatchEltsFilled, + NNResultBuf** inputBufs, + vector& outputs) { + int batchSize = numBatchEltsFilled; + auto coremlbackend = gpuHandle->coremlbackend; + int nnXLen = gpuHandle->nnXLen; + int nnYLen = gpuHandle->nnYLen; + int modelXLen = gpuHandle->modelXLen; + int modelYLen = gpuHandle->modelYLen; + int version = gpuHandle->modelVersion; + int numSpatialFeatures = NNModelVersion::getNumSpatialFeatures(version); + size_t singleSpatialElts = inputBuffers->singleSpatialElts; + size_t singleInputElts = numSpatialFeatures * modelXLen * modelYLen; + size_t singleInputGlobalElts = inputBuffers->singleInputGlobalElts; + size_t singleInputMetaElts = inputBuffers->singleInputMetaElts; + + assert(batchSize <= inputBuffers->maxBatchSize); + assert(batchSize > 0); + assert(coremlbackend); + // Model board length must be not larger than the maximum board length + assert(singleInputElts <= inputBuffers->singleInputElts); + assert(NNModelVersion::getNumGlobalFeatures(version) == inputBuffers->singleInputGlobalElts); + assert(version == coremlbackend.get().getVersion()); + assert(singleInputGlobalElts == 19); + assert(inputBuffers->singleModelPolicyResultElts >= ((modelXLen * modelYLen) + 1)); + assert(inputBuffers->singleValueResultElts == 3); + assert(inputBuffers->singleModelOwnershipResultElts >= (modelXLen * modelYLen)); + assert(inputBuffers->singleScoreValuesResultElts == 10); + assert(inputBuffers->singleMoreMiscValuesResultElts == 8); + assert(gpuHandle->inputsUseNHWC == false); + + for(size_t row = 0; row < batchSize; row++) { + float* rowSpatialBuffer = &inputBuffers->rowSpatialBuffer[singleSpatialElts * row]; + float* rowSpatialInput = &inputBuffers->userInputBuffer[singleInputElts * row]; + float* rowGlobalInput = &inputBuffers->userInputGlobalBuffer[singleInputGlobalElts * row]; + float* rowMetaInput = &inputBuffers->userInputMetaBuffer[singleInputMetaElts * row]; + const float* rowGlobal = inputBufs[row]->rowGlobalBuf.data(); + const float* rowSpatial = inputBufs[row]->rowSpatialBuf.data(); + const float* rowMeta = inputBufs[row]->rowMetaBuf.data(); + + std::copy(&rowGlobal[0], &rowGlobal[singleInputGlobalElts], rowGlobalInput); + std::copy(&rowMeta[0], &rowMeta[singleInputMetaElts], rowMetaInput); + + SymmetryHelpers::copyInputsWithSymmetry( + rowSpatial, + rowSpatialBuffer, + 1, + nnYLen, + nnXLen, + numSpatialFeatures, + gpuHandle->inputsUseNHWC, + inputBufs[row]->symmetry); + + for(int c = 0; c < numSpatialFeatures; c++) { + for(int y = 0; y < nnYLen; y++) { + for(int x = 0; x < nnXLen; x++) { + int bufferIdx = (c * nnYLen * nnXLen) + (y * nnXLen) + x; + int inputIdx = (c * modelYLen * modelXLen) + (y * modelXLen) + x; + rowSpatialInput[inputIdx] = rowSpatialBuffer[bufferIdx]; + } + } + } + } + + coremlbackend.get().getBatchOutput(inputBuffers->userInputBuffer, + inputBuffers->userInputGlobalBuffer, + inputBuffers->userInputMetaBuffer, + inputBuffers->modelPolicyResults, + inputBuffers->valueResults, + inputBuffers->ownershipResults, + inputBuffers->scoreValuesResults, + inputBuffers->moreMiscValuesResults, + batchSize); + + // Fill results by CoreML model output + for(size_t row = 0; row < batchSize; row++) { + NNOutput* output = outputs[row]; + CoreMLProcess::processPolicy(inputBuffers, output, gpuHandle, inputBufs[row], row); + CoreMLProcess::processValue(inputBuffers, output, row); + CoreMLProcess::processOwnership(inputBuffers, output, gpuHandle, inputBufs[row]->symmetry, row); + CoreMLProcess::processScoreValues(inputBuffers, output, version, row); + } +} + +#endif // USE_COREML_BACKEND diff --git a/cpp/neuralnet/coremlbackend.h b/cpp/neuralnet/coremlbackend.h new file mode 100644 index 000000000..7d33c1085 --- /dev/null +++ b/cpp/neuralnet/coremlbackend.h @@ -0,0 +1,51 @@ +#ifndef coremlbackend_h +#define coremlbackend_h + +#include "../neuralnet/modelversion.h" +#include "../neuralnet/nneval.h" +#include "../neuralnet/nninputs.h" +#include "../neuralnet/nninterface.h" + +using namespace std; + +namespace CoreMLProcess { + size_t calculateBufferOffset(size_t row, size_t singleResultElts, size_t resultChannels); + int calculateIndex(const int y, const int x, const int xLen); + float policyOptimismCalc(const double policyOptimism, const float p, const float pOpt); + + float assignPolicyValue( + const size_t policyResultChannels, + const double policyOptimism, + const float* targetBuffer, + const size_t outputIdx, + const size_t singleModelPolicyResultElts); + + void processPolicy( + InputBuffers* inputBuffers, + NNOutput* currentOutput, + const ComputeHandle* gpuHandle, + NNResultBuf* inputBuf, + size_t row); + + void processValue(const InputBuffers* inputBuffers, NNOutput* currentOutput, const size_t row); + + void processOwnership( + const InputBuffers* inputBuffers, + NNOutput* currentOutput, + const ComputeHandle* gpuHandle, + const int symmetry, + const size_t row); + + void + processScoreValues(const InputBuffers* inputBuffers, NNOutput* currentOutput, const int version, const size_t row); + + void getCoreMLOutput( + ComputeHandle* gpuHandle, + InputBuffers* inputBuffers, + int numBatchEltsFilled, + NNResultBuf** inputBufs, + vector& outputs); + +}; + +#endif /* coremlbackend_h */ diff --git a/cpp/neuralnet/coremlbackend.swift b/cpp/neuralnet/coremlbackend.swift new file mode 100644 index 000000000..4fafec43b --- /dev/null +++ b/cpp/neuralnet/coremlbackend.swift @@ -0,0 +1,248 @@ +// +// coremlbackend.swift +// KataGo +// +// Created by Chin-Chang Yang on 2023/11/8. +// + +import Foundation +import CoreML + +extension MLModel { + var version: Int32 { + let versionString = modelDescription.metadata[MLModelMetadataKey.versionString] as! String + let versionInt = Int32(versionString)! + return versionInt + } + + var metaDescription: String { + let description = modelDescription.metadata[MLModelMetadataKey.description] as! String + return description + } + + var nnXLen: Int? { + if let match = metaDescription.firstMatch(of: #/KataGo\s+(\d+)x(\d+)/#) { + let nnXLen = Int(match.1) + return nnXLen + } else { + return nil + } + } + + var nnYLen: Int? { + if let match = metaDescription.firstMatch(of: #/KataGo\s+(\d+)x(\d+)/#) { + let nnYLen = Int(match.2) + return nnYLen + } else { + return nil + } + } +} + +public class CoreMLBackend { + + class func getModelName(xLen: Int = 19, + yLen: Int = 19, + useFP16: Bool = true, + metaEncoderVersion: Int = 0) -> String { + let precision = useFP16 ? 16 : 32 + let encoder = (metaEncoderVersion > 0) ? "m\(metaEncoderVersion)" : "" + return "KataGoModel\(xLen)x\(yLen)fp\(precision)\(encoder)" + } + + var model: KataGoModel + let xLen: Int + let yLen: Int + public let version: Int32 + let numSpatialFeatures: Int + let numGlobalFeatures: Int + let numMetaFeatures: Int + let metaEncoderVersion: Int + let modelName: String + let modelDirectory: String + + var spatialSize: Int { + numSpatialFeatures * yLen * xLen + } + + public var modelXLen: Int32 { Int32(xLen) } + public var modelYLen: Int32 { Int32(yLen) } + + init(model: MLModel, metaEncoderVersion: Int, modelName: String, modelDirectory: String) { + self.model = KataGoModel(model: model) + self.metaEncoderVersion = metaEncoderVersion + self.modelName = modelName + self.modelDirectory = modelDirectory + + self.xLen = model.nnXLen ?? 19 + assert(self.xLen >= 2) + + self.yLen = model.nnYLen ?? 19 + assert(self.yLen >= 2) + + // The model version must be at least 8. + self.version = model.version + assert(self.version >= 8) + + // The number of spatial features must be 22. + self.numSpatialFeatures = 22 + + // The number of global features must be 19. + self.numGlobalFeatures = 19 + + // The number of meta features must be 192. + self.numMetaFeatures = 192 + } + + public func getBatchOutput(binInputs: UnsafeMutablePointer, + globalInputs: UnsafeMutablePointer, + metaInputs: UnsafeMutablePointer, + policyOutputs: UnsafeMutablePointer, + valueOutputs: UnsafeMutablePointer, + ownershipOutputs: UnsafeMutablePointer, + miscValuesOutputs: UnsafeMutablePointer, + moreMiscValuesOutputs: UnsafeMutablePointer, + batchSize: Int) { + + autoreleasepool { + let spatialStrides = [numSpatialFeatures * yLen * xLen, + yLen * xLen, + xLen, + 1] as [NSNumber] + + let globalStrides = [numGlobalFeatures, 1] as [NSNumber] + + let inputArray = (0.. KataGoModelInput in + let binInputsArray = try! MLMultiArray( + dataPointer: binInputs.advanced(by: index * spatialSize), + shape: [1, numSpatialFeatures, yLen, xLen] as [NSNumber], + dataType: .float, + strides: spatialStrides) + + let globalInputsArray = try! MLMultiArray( + dataPointer: globalInputs.advanced(by: index * numGlobalFeatures), + shape: [1, numGlobalFeatures] as [NSNumber], + dataType: .float, + strides: globalStrides) + + if metaEncoderVersion == 0 { + return KataGoModelInput(input_spatial: binInputsArray, input_global: globalInputsArray) + } else { + let metaStrides = [numMetaFeatures, 1] as [NSNumber] + + let metaInputsArray = try! MLMultiArray( + dataPointer: metaInputs.advanced(by: index * numMetaFeatures), + shape: [1, numMetaFeatures] as [NSNumber], + dataType: .float, + strides: metaStrides) + + return KataGoModelInput(input_spatial: binInputsArray, + input_global: globalInputsArray, + input_meta: metaInputsArray) + } + } + + let inputBatch = KataGoModelInputBatch(inputArray: inputArray) + let options = MLPredictionOptions() + + let outputBatch = safelyPredict(from: inputBatch, options: options)! + assert(outputBatch.count == batchSize) + + outputBatch.outputArray.enumerated().forEach { index, output in + let policyOutputBase = policyOutputs.advanced(by: index * output.output_policy.count) + let valueOutputBase = valueOutputs.advanced(by: index * output.out_value.count) + let ownershipOutputBase = ownershipOutputs.advanced(by: index * output.out_ownership.count) + let miscValuesOutputBase = miscValuesOutputs.advanced(by: index * output.out_miscvalue.count) + let moreMiscValuesOutputBase = moreMiscValuesOutputs.advanced(by: index * output.out_moremiscvalue.count) + + (0.. KataGoModelOutputBatch? { + if let prediction = try? model.prediction(from: inputBatch, options: options) { + return prediction + } + + let computeUnits = model.model.configuration.computeUnits + + for mustCompile in [false, true] { + if let prediction = compileAndPredict(with: computeUnits, from: inputBatch, options: options, mustCompile: mustCompile) { + return prediction + } + } + + return nil + } + + private func compileAndPredict(with computeUnits: MLComputeUnits, + from inputBatch: KataGoModelInputBatch, + options: MLPredictionOptions, + mustCompile: Bool) -> KataGoModelOutputBatch? { + if let mlmodel = KataGoModel.compileBundleMLModel(modelName: modelName, computeUnits: computeUnits, mustCompile: mustCompile, modelDirectory: modelDirectory) { + model = KataGoModel(model: mlmodel) + if let outputBatch = try? model.prediction(from: inputBatch, options: options) { + return outputBatch + } + } + + return nil + } +} + +public func maybeCreateCoreMLBackend(condition: Bool = true, + serverThreadIdx: Int = 0, + xLen: Int = 19, + yLen: Int = 19, + useFP16: Bool = false, + metaEncoderVersion: Int = 0, + useCpuAndNeuralEngine: Bool = true, + modelDirectory: String = "") -> CoreMLBackend? { + guard condition else { return nil } + + // Get the model name. + let modelName = CoreMLBackend.getModelName(xLen: xLen, yLen: yLen, useFP16: useFP16, metaEncoderVersion: metaEncoderVersion) + + // Specify compute units. + let computeUnits: MLComputeUnits = useCpuAndNeuralEngine ? .cpuAndNeuralEngine : .all + + // Compile the model in Bundle. + let mlmodel = KataGoModel.compileBundleMLModel(modelName: modelName, + computeUnits: computeUnits, + mustCompile: false, + modelDirectory: modelDirectory) + + if let mlmodel { + printError("CoreML backend \(serverThreadIdx): useFP16 \(useFP16) metaEncoderVersion \(metaEncoderVersion) useCpuAndNeuralEngine \(useCpuAndNeuralEngine)"); + printError("CoreML backend \(serverThreadIdx): \(mlmodel.metaDescription)"); + + // The CoreMLBackend object is created. + return CoreMLBackend(model: mlmodel, + metaEncoderVersion: metaEncoderVersion, + modelName: modelName, + modelDirectory: modelDirectory) + } else { + printError("Unable to compile bundle MLModel from model: \(modelName)") + return nil + } +} diff --git a/cpp/neuralnet/coremlmodel.swift b/cpp/neuralnet/coremlmodel.swift new file mode 100644 index 000000000..1464a8b1c --- /dev/null +++ b/cpp/neuralnet/coremlmodel.swift @@ -0,0 +1,321 @@ +// +// coremlmodel.swift +// KataGo +// +// Created by Chin-Chang Yang on 2023/11/7. +// + +import CryptoKit +import Foundation +import CoreML + +class KataGoModelInput: MLFeatureProvider { + var input_spatial: MLMultiArray + var input_global: MLMultiArray + var input_meta: MLMultiArray? + + var featureNames: Set { + return Set(["input_spatial", "input_global", "input_meta"]) + } + + init(input_spatial: MLMultiArray, input_global: MLMultiArray) { + self.input_spatial = input_spatial + self.input_global = input_global + } + + init(input_spatial: MLMultiArray, input_global: MLMultiArray, input_meta: MLMultiArray) { + self.input_spatial = input_spatial + self.input_global = input_global + self.input_meta = input_meta + } + + func featureValue(for featureName: String) -> MLFeatureValue? { + if (featureName == "input_spatial") { + return MLFeatureValue(multiArray: input_spatial) + } else if (featureName == "input_global") { + return MLFeatureValue(multiArray: input_global) + } else if (featureName == "input_meta"), let input_meta { + return MLFeatureValue(multiArray: input_meta) + } else { + return nil + } + } +} + +class KataGoModelInputBatch: MLBatchProvider { + var inputArray: [KataGoModelInput] + + var count: Int { + inputArray.count + } + + func features(at index: Int) -> MLFeatureProvider { + return inputArray[index] + } + + init(inputArray: [KataGoModelInput]) { + self.inputArray = inputArray + } +} + +class KataGoModelOutput { + var output_policy: MLMultiArray + var out_value: MLMultiArray + var out_miscvalue: MLMultiArray + var out_moremiscvalue: MLMultiArray + var out_ownership: MLMultiArray + + init(output_policy: MLMultiArray, + out_value: MLMultiArray, + out_miscvalue: MLMultiArray, + out_moremiscvalue: MLMultiArray, + out_ownership: MLMultiArray) { + self.output_policy = output_policy + self.out_value = out_value + self.out_miscvalue = out_miscvalue + self.out_moremiscvalue = out_moremiscvalue + self.out_ownership = out_ownership + } +} + +class KataGoModelOutputBatch { + var outputArray: [KataGoModelOutput] + + var count: Int { + outputArray.count + } + + init(outputArray: [KataGoModelOutput]) { + self.outputArray = outputArray + } +} + +class KataGoModel { + let model: MLModel + + class func getBundleModelURL(modelName: String, modelDirectory: String) -> URL { + // Set model type name + let typeName = "mlpackage" + // Get model path from bundle resource + // Fallback to create a default model path + let modelPath = Bundle.main.path(forResource: modelName, ofType: typeName) ?? "\(modelName).\(typeName)" + // If modelDirectory is not empty, prepend it to the modelPath + let finalPath = modelDirectory.isEmpty ? modelPath : modelDirectory + let bundleModelURL = URL(filePath: finalPath) + + return bundleModelURL + } + + class func compileBundleMLModel(modelName: String, + computeUnits: MLComputeUnits, + mustCompile: Bool = false, + modelDirectory: String = "") -> MLModel? { + var mlmodel: MLModel? + + do { + // Get model URL at bundle + let bundleModelURL = getBundleModelURL(modelName: modelName, modelDirectory: modelDirectory) + + // Compile MLModel + mlmodel = try compileMLModel(modelName: modelName, + modelURL: bundleModelURL, + computeUnits: computeUnits, + mustCompile: mustCompile) + } catch { + printError("An error occurred: \(error)") + } + + return mlmodel; + } + + private class func getApplicationSupportURL() throws -> URL { + // Get default file manager + let fileManager = FileManager.default + + return try fileManager.url(for: .applicationSupportDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: true) + } + + private class func getDigest(modelURL: URL) throws -> String { + // Create the URL for the model data file + let dataURL = modelURL.appending(component: "Data/com.apple.CoreML/model.mlmodel") + + // Get model data + let modelData = try Data(contentsOf: dataURL) + + // Get SHA256 data + let hashData = Data(SHA256.hash(data: modelData).makeIterator()) + + // Get hash digest + let digest = hashData.map { String(format: "%02x", $0) }.joined() + + return digest + } + + private class func checkShouldCompileModel(permanentURL: URL, + savedDigestURL: URL, + digest: String) -> Bool { + // Model should be compiled if the compiled model is not reachable or the digest changes + var shouldCompile = true + + // Get saved digest + do { + if (try savedDigestURL.checkResourceIsReachable()) { + let savedDigest = try String(contentsOf: savedDigestURL, encoding: .utf8) + + // Check the saved digest is changed or not + shouldCompile = digest != savedDigest + + if (shouldCompile) { + printError("Saved digest: \(savedDigest)") + printError("New digest: \(digest)") + printError("Compiling CoreML model because the digest has changed"); + } else { + printError("Digests match: \(digest)") + } + } else { + printError("Compiling CoreML model because the saved digest URL is not reachable: \(savedDigestURL)") + } + } catch { + printError("Compiling CoreML model because it is unable to get the saved digest from: \(savedDigestURL)") + } + + if !shouldCompile { + // Check permanent compiled model is reachable + do { + // This method is currently applicable only to URLs for file system + // resources. For other URL types, `false` is returned. + shouldCompile = try (!permanentURL.checkResourceIsReachable()) + assert(!shouldCompile) + + printError("Compiled CoreML model is reachable: \(permanentURL)") + } catch { + shouldCompile = true + + printError("Compiling CoreML model because it is unable to check the resource at: \(permanentURL)") + } + } + + return shouldCompile + } + + private class func compileAndSaveModel(permanentURL: URL, + savedDigestURL: URL, + modelURL: URL, + digest: String) throws { + // Get default file manager + let fileManager = FileManager.default + + printError("Compiling CoreML model at \(modelURL)"); + + // Compile the model + let compiledURL = try MLModel.compileModel(at: modelURL) + + printError("Creating the directory for the permanent location: \(permanentURL)"); + + // Create the directory for KataGo models + try fileManager.createDirectory(at: permanentURL.deletingLastPathComponent(), + withIntermediateDirectories: true) + + printError("Copying the compiled CoreML model to the permanent location \(permanentURL)"); + + // Copy the file to the to the permanent location, replacing it if necessary + try fileManager.replaceItem(at: permanentURL, + withItemAt: compiledURL, + backupItemName: nil, + options: .usingNewMetadataOnly, + resultingItemURL: nil) + + printError("Writing digest to: \(savedDigestURL)") + printError("Digest: \(digest)") + + // Update the digest + try digest.write(to: savedDigestURL, atomically: true, encoding: .utf8) + } + + private class func loadModel(permanentURL: URL, modelName: String, computeUnits: MLComputeUnits) throws -> MLModel { + let configuration = MLModelConfiguration() + configuration.computeUnits = computeUnits + configuration.modelDisplayName = modelName + printError("Creating CoreML model with contents \(permanentURL)") + return try MLModel(contentsOf: permanentURL, configuration: configuration) + } + + class func getMLModelCPermanentURL(modelName: String) throws -> URL { + let appSupportURL = try getApplicationSupportURL() + let permanentURL = appSupportURL.appending(component: "KataGoModels/\(modelName).mlmodelc") + + return permanentURL + } + + class func getSavedDigestURL(modelName: String) throws -> URL { + let appSupportURL = try getApplicationSupportURL() + let savedDigestURL = appSupportURL.appending(component: "KataGoModels/\(modelName).digest") + + return savedDigestURL + } + + class func compileMLModel(modelName: String, + modelURL: URL, + computeUnits: MLComputeUnits, + mustCompile: Bool) throws -> MLModel { + + let permanentURL = try getMLModelCPermanentURL(modelName: modelName) + let savedDigestURL = try getSavedDigestURL(modelName: modelName) + let digest = try getDigest(modelURL: modelURL) + + var shouldCompile: Bool + + if mustCompile { + shouldCompile = true + } else { + shouldCompile = checkShouldCompileModel(permanentURL: permanentURL, + savedDigestURL: savedDigestURL, + digest: digest) + } + + if shouldCompile { + try compileAndSaveModel(permanentURL: permanentURL, + savedDigestURL: savedDigestURL, + modelURL: modelURL, + digest: digest) + } + + return try loadModel(permanentURL: permanentURL, + modelName: modelName, + computeUnits: computeUnits); + } + + init(model: MLModel) { + self.model = model + } + + private func createOutput(from outFeatures: MLFeatureProvider) -> KataGoModelOutput { + + let output_policy = (outFeatures.featureValue(for: "output_policy")?.multiArrayValue)! + let out_value = (outFeatures.featureValue(for: "out_value")?.multiArrayValue)! + let out_miscvalue = (outFeatures.featureValue(for: "out_miscvalue")?.multiArrayValue)! + let out_moremiscvalue = (outFeatures.featureValue(for: "out_moremiscvalue")?.multiArrayValue)! + let out_ownership = (outFeatures.featureValue(for: "out_ownership")?.multiArrayValue)! + + return KataGoModelOutput(output_policy: output_policy, + out_value: out_value, + out_miscvalue: out_miscvalue, + out_moremiscvalue: out_moremiscvalue, + out_ownership: out_ownership) + } + + func prediction(from inputBatch: KataGoModelInputBatch, + options: MLPredictionOptions) throws -> KataGoModelOutputBatch { + + let outFeaturesBatch = try model.predictions(from: inputBatch, options: options) + let outputArray = (0.. KataGoModelOutput in + let outFeatures = outFeaturesBatch.features(at: index) + return createOutput(from: outFeatures) + } + + return KataGoModelOutputBatch(outputArray: outputArray) + } +} diff --git a/cpp/neuralnet/cudabackend.cpp b/cpp/neuralnet/cudabackend.cpp index c76a3dab9..6ae07d338 100644 --- a/cpp/neuralnet/cudabackend.cpp +++ b/cpp/neuralnet/cudabackend.cpp @@ -2133,8 +2133,9 @@ struct LoadedModel { LoadedModel& operator=(const LoadedModel&) = delete; }; -LoadedModel* NeuralNet::loadModelFile(const string& file, const string& expectedSha256) { +LoadedModel* NeuralNet::loadModelFile(const string& file, const string& expectedSha256, const string& dir) { LoadedModel* loadedModel = new LoadedModel(file,expectedSha256); + (void)dir; return loadedModel; } diff --git a/cpp/neuralnet/dummybackend.cpp b/cpp/neuralnet/dummybackend.cpp index 3325894f5..900f42fde 100644 --- a/cpp/neuralnet/dummybackend.cpp +++ b/cpp/neuralnet/dummybackend.cpp @@ -42,9 +42,10 @@ void NeuralNet::freeComputeContext(ComputeContext* computeContext) { throw StringError("Dummy neural net backend: NeuralNet::freeComputeContext unimplemented"); } -LoadedModel* NeuralNet::loadModelFile(const string& file, const string& expectedSha256) { +LoadedModel* NeuralNet::loadModelFile(const string& file, const string& expectedSha256, const string& dir) { (void)file; (void)expectedSha256; + (void)dir; throw StringError("Dummy neural net backend: NeuralNet::loadModelFile unimplemented"); } diff --git a/cpp/neuralnet/eigenbackend.cpp b/cpp/neuralnet/eigenbackend.cpp index 9b5d714c3..8f9fc7211 100644 --- a/cpp/neuralnet/eigenbackend.cpp +++ b/cpp/neuralnet/eigenbackend.cpp @@ -83,8 +83,9 @@ struct LoadedModel { LoadedModel& operator=(const LoadedModel&) = delete; }; -LoadedModel* NeuralNet::loadModelFile(const string& file, const string& expectedSha256) { +LoadedModel* NeuralNet::loadModelFile(const string& file, const string& expectedSha256, const string& dir) { LoadedModel* loadedModel = new LoadedModel(file,expectedSha256); + (void)dir; return loadedModel; } diff --git a/cpp/neuralnet/metalbackend.cpp b/cpp/neuralnet/metalbackend.cpp index bdb00b449..78072b0f2 100644 --- a/cpp/neuralnet/metalbackend.cpp +++ b/cpp/neuralnet/metalbackend.cpp @@ -1,10 +1,11 @@ -#ifdef USE_METAL_BACKEND +#ifdef USE_COREML_BACKEND #include "../neuralnet/modelversion.h" #include "../neuralnet/nneval.h" #include "../neuralnet/nninputs.h" #include "../neuralnet/nninterface.h" #include "../neuralnet/metalbackend.h" +#include "../neuralnet/coremlbackend.h" #include "../core/test.h" /// Converts a ConvLayerDesc instance from C++ to Swift by creating a new SWConvLayerDesc instance with the same properties. @@ -349,10 +350,11 @@ void NeuralNet::globalCleanup() { * object is returned as a pointer. * @param file The name of the file containing the neural network model. * @param expectedSha256 The expected SHA-256 hash of the model file. + * @param dir The name of the directory containing the neural network model. * @return A pointer to the LoadedModel object created by loading the model file. */ -LoadedModel* NeuralNet::loadModelFile(const string& file, const string& expectedSha256) { - LoadedModel* loadedModel = new LoadedModel(file, expectedSha256); +LoadedModel* NeuralNet::loadModelFile(const string& file, const string& expectedSha256, const string& dir) { + LoadedModel* loadedModel = new LoadedModel(file, expectedSha256, dir); return loadedModel; } @@ -383,9 +385,10 @@ const ModelDesc& NeuralNet::getModelDesc(const LoadedModel* loadedModel) { //------------------------------------------------------------------------------ -ComputeContext::ComputeContext(int nnX, int nnY, enabled_t useFP16Mode, enabled_t useNHWCMode): +ComputeContext::ComputeContext(int nnX, int nnY, enabled_t useFP16Mode, enabled_t useNHWCMode, bool useCpuAndNeuralEngine): metalComputeContext(createMetalComputeContext(nnX, nnY)) { this->useFP16Mode = useFP16Mode; + this->useCpuAndNeuralEngine = useCpuAndNeuralEngine; SWEnable swUseFP16Mode = (useFP16Mode == enabled_t::False) ? SWEnable::False() : @@ -430,14 +433,26 @@ ComputeContext* NeuralNet::createComputeContext( enabled_t useNHWCMode, const LoadedModel* loadedModel) { - (void)gpuIdxs; + bool useCpuAndNeuralEngine = false; + + // If Metal is enabled for GPU computation, CoreML uses CPU and Neural Engine. + // If Metal is disabled, CoreML uses all computation units, including CPU, GPU, and Neural Engine. + // This ensures that Metal and CoreML do not use GPU in the same computation context. + for (auto it = gpuIdxs.begin(); it != gpuIdxs.end(); it++) { + auto gpuIdx = *it; + if (gpuIdx < 100) { + useCpuAndNeuralEngine = true; + break; + } + } + (void)logger; (void)openCLTunerFile; (void)homeDataDirOverride; (void)openCLReTunePerBoardSize; (void)loadedModel; - return new ComputeContext(nnXLen, nnYLen, useFP16Mode, useNHWCMode); + return new ComputeContext(nnXLen, nnYLen, useFP16Mode, useNHWCMode, useCpuAndNeuralEngine); } /** @@ -459,8 +474,15 @@ ComputeHandle::ComputeHandle(ComputeContext* context, metalhandle(maybeCreateMetalComputeHandle((gpuIdx < 100), serverThreadIdx, MetalProcess::modelDescToSwift(&loadedModel->modelDesc), - context->metalComputeContext)) { - + context->metalComputeContext)), +coremlbackend(maybeCreateCoreMLBackend((gpuIdx >= 100), + serverThreadIdx, + COMPILE_MAX_BOARD_LEN, + COMPILE_MAX_BOARD_LEN, + (context->useFP16Mode != enabled_t::False), + loadedModel->modelDesc.metaEncoderVersion, + context->useCpuAndNeuralEngine, + loadedModel->modelDirectory)) { const ModelDesc* modelDesc = &loadedModel->modelDesc; auto metalContext = context->metalComputeContext; @@ -475,6 +497,19 @@ metalhandle(maybeCreateMetalComputeHandle((gpuIdx < 100), * disabled it. */ useFP16 = (context->useFP16Mode != enabled_t::False); + if(coremlbackend) { + // Get the model version + modelVersion = coremlbackend.get().getVersion(); + // Due to a design limition, the versions of Metal and CoreML models must match + assert(version == modelVersion); + + // Model board length must be not smaller than net board length + modelXLen = coremlbackend.get().getModelXLen(); + modelYLen = coremlbackend.get().getModelYLen(); + assert(nnXLen <= modelXLen); + assert(nnYLen <= modelYLen); + } + (void)serverThreadIdx; } @@ -569,6 +604,9 @@ void NeuralNet::printDevices() { InputBuffers::InputBuffers(const LoadedModel* loadedModel, int maxBatchSz, int nnXLen, int nnYLen) { const ModelDesc& m = loadedModel->modelDesc; + int maxModelXLen = COMPILE_MAX_BOARD_LEN; + int maxModelYLen = COMPILE_MAX_BOARD_LEN; + maxBatchSize = maxBatchSz; policyResultChannels = m.policyHead.p2Conv.outChannels; @@ -576,17 +614,22 @@ InputBuffers::InputBuffers(const LoadedModel* loadedModel, int maxBatchSz, int n ((m.modelVersion >= 16) || (m.modelVersion < 12) || (policyResultChannels == 2)) && ((m.modelVersion >= 12) || (policyResultChannels == 1))); + modelPolicyResultChannels = (m.modelVersion >= 12) ? 6 : 4; singleSpatialElts = (size_t)m.numInputChannels * nnXLen * nnYLen; - singleInputElts = (size_t)m.numInputChannels * nnXLen * nnYLen; + singleInputElts = (size_t)m.numInputChannels * maxModelXLen * maxModelYLen; singleInputGlobalElts = (size_t)m.numInputGlobalChannels; singleInputMetaElts = (size_t)m.numInputMetaChannels; - singlePolicyResultElts = (size_t)(nnXLen * nnYLen); + singleNnPolicyResultElts = (size_t)(nnXLen * nnYLen); + singleModelPolicyResultElts = (size_t)((maxModelXLen * maxModelYLen) + 1); singlePolicyPassResultElts = 1; singlePolicyProbsElts = (size_t)((nnXLen * nnYLen) + 1); singleValueResultElts = (size_t)m.numValueChannels; - singleOwnershipResultElts = (size_t)m.numOwnershipChannels * nnXLen * nnYLen; + singleNnOwnershipResultElts = (size_t)m.numOwnershipChannels * nnXLen * nnYLen; + singleModelOwnershipResultElts = (size_t)m.numOwnershipChannels * maxModelXLen * maxModelYLen; singleOwnerMapElts = (size_t)m.numOwnershipChannels * nnXLen * nnYLen; - singleScoreValuesResultElts = (size_t)m.numScoreValueChannels; + singleScoreValuesResultElts = 10; + singleNnScoreValuesResultElts = (size_t)m.numScoreValueChannels; + singleMoreMiscValuesResultElts = 8; assert(NNModelVersion::getNumSpatialFeatures(m.modelVersion) == m.numInputChannels); assert(NNModelVersion::getNumGlobalFeatures(m.modelVersion) == m.numInputGlobalChannels); @@ -596,13 +639,15 @@ InputBuffers::InputBuffers(const LoadedModel* loadedModel, int maxBatchSz, int n userInputBufferElts = (size_t)maxBatchSize * singleInputElts; userInputGlobalBufferElts = (size_t)maxBatchSize * singleInputGlobalElts; userInputMetaBufferElts = (size_t)maxBatchSize * singleInputMetaElts; - policyResultBufferElts = (size_t)maxBatchSize * singlePolicyResultElts * policyResultChannels; + policyResultBufferElts = (size_t)maxBatchSize * singleModelPolicyResultElts * policyResultChannels; policyPassResultBufferElts = (size_t)maxBatchSize * singlePolicyPassResultElts * policyResultChannels; policyProbsBufferElts = (size_t)maxBatchSize * singlePolicyProbsElts * policyResultChannels; + modelPolicyResultBufferElts = (size_t)maxBatchSize * singleModelPolicyResultElts * modelPolicyResultChannels; valueResultBufferElts = (size_t)maxBatchSize * singleValueResultElts; - ownershipResultBufferElts = (size_t)maxBatchSize * singleOwnershipResultElts; + ownershipResultBufferElts = (size_t)maxBatchSize * singleModelOwnershipResultElts; ownerMapBufferElts = (size_t)maxBatchSz * singleOwnerMapElts; scoreValuesResultBufferElts = (size_t)maxBatchSize * singleScoreValuesResultElts; + moreMiscValuesResultsBufferElts = (size_t)maxBatchSz * singleMoreMiscValuesResultElts; rowSpatialBuffer = new float[rowSpatialBufferElts]; userInputBuffer = new float[userInputBufferElts]; @@ -614,10 +659,12 @@ InputBuffers::InputBuffers(const LoadedModel* loadedModel, int maxBatchSz, int n policyResults = new float[policyResultBufferElts]; policyPassResults = new float[policyPassResultBufferElts]; policyProbsBuffer = new float[policyProbsBufferElts]; + modelPolicyResults = new float[modelPolicyResultBufferElts]; valueResults = new float[valueResultBufferElts]; ownershipResults = new float[ownershipResultBufferElts]; ownerMapBuffer = new float[ownerMapBufferElts]; scoreValuesResults = new float[scoreValuesResultBufferElts]; + moreMiscValuesResults = new float[moreMiscValuesResultsBufferElts]; } /** @@ -637,6 +684,7 @@ InputBuffers::~InputBuffers() { delete[] ownershipResults; delete[] ownerMapBuffer; delete[] scoreValuesResults; + delete[] moreMiscValuesResults; } /** @@ -778,7 +826,7 @@ void MetalProcess::processOptimism( const double policyOptimism, size_t row) { auto& buffers = *inputBuffers; - const auto singlePolicyResultElts = buffers.singlePolicyResultElts; + const auto singlePolicyResultElts = buffers.singleNnPolicyResultElts; float* targetBuffer = &buffers.policyProbsBuffer[row * singlePolicyResultElts]; float* policyOutputBuf = &buffers.policyResults[row * singlePolicyResultElts * buffers.policyResultChannels]; @@ -800,7 +848,7 @@ void MetalProcess::processPolicy( NNResultBuf* inputBuf, size_t row) { auto& buffers = *inputBuffers; - float* targetBuffer = &buffers.policyResults[row * buffers.singlePolicyResultElts * buffers.policyResultChannels]; + float* targetBuffer = &buffers.policyResults[row * buffers.singleNnPolicyResultElts * buffers.policyResultChannels]; const auto symmetry = inputBuf->symmetry; const auto policyOptimism = inputBuf->policyOptimism; @@ -809,7 +857,7 @@ void MetalProcess::processPolicy( buffers.policyPassResults[row * buffers.policyResultChannels]; } else { MetalProcess::processOptimism(inputBuffers, currentOutput, policyOptimism, row); - targetBuffer = &buffers.policyProbsBuffer[row * buffers.singlePolicyResultElts]; + targetBuffer = &buffers.policyProbsBuffer[row * buffers.singleNnPolicyResultElts]; } SymmetryHelpers::copyOutputsWithSymmetry( @@ -836,7 +884,7 @@ void MetalProcess::processOwnership( const size_t row) { const int nnXLen = gpuHandle->nnXLen; const int nnYLen = gpuHandle->nnYLen; - const size_t singleOwnershipResultElts = inputBuffers->singleOwnershipResultElts; + const size_t singleOwnershipResultElts = inputBuffers->singleNnOwnershipResultElts; const size_t ownershipOutputBufOffset = row * singleOwnershipResultElts; // Copy ownership results with symmetry if available @@ -852,11 +900,11 @@ void MetalProcess::processScoreValues( NNOutput* currentOutput, const int modelVersion, const size_t row) { - const size_t offset = row * inputBuffers->singleScoreValuesResultElts; + const size_t offset = row * inputBuffers->singleNnScoreValuesResultElts; const float* currentScoreValueData = &inputBuffers->scoreValuesResults[offset]; if(modelVersion >= 9) { - int numScoreValueChannels = inputBuffers->singleScoreValuesResultElts; + size_t numScoreValueChannels = inputBuffers->singleNnScoreValuesResultElts; assert(numScoreValueChannels == 6); currentOutput->whiteScoreMean = currentScoreValueData[0]; currentOutput->whiteScoreMeanSq = currentScoreValueData[1]; @@ -866,7 +914,7 @@ void MetalProcess::processScoreValues( currentOutput->shorttermScoreError = currentScoreValueData[5]; } else if(modelVersion >= 8) { - int numScoreValueChannels = inputBuffers->singleScoreValuesResultElts; + size_t numScoreValueChannels = inputBuffers->singleNnScoreValuesResultElts; assert(numScoreValueChannels == 4); currentOutput->whiteScoreMean = currentScoreValueData[0]; currentOutput->whiteScoreMeanSq = currentScoreValueData[1]; @@ -876,7 +924,7 @@ void MetalProcess::processScoreValues( currentOutput->shorttermScoreError = 0; } else if(modelVersion >= 4) { - int numScoreValueChannels = inputBuffers->singleScoreValuesResultElts; + size_t numScoreValueChannels = inputBuffers->singleNnScoreValuesResultElts; assert(numScoreValueChannels == 2); currentOutput->whiteScoreMean = currentScoreValueData[0]; currentOutput->whiteScoreMeanSq = currentScoreValueData[1]; @@ -887,7 +935,7 @@ void MetalProcess::processScoreValues( } else { assert(modelVersion >= 3); - int numScoreValueChannels = inputBuffers->singleScoreValuesResultElts; + size_t numScoreValueChannels = inputBuffers->singleNnScoreValuesResultElts; assert(numScoreValueChannels == 1); currentOutput->whiteScoreMean = currentScoreValueData[0]; //Version 3 neural nets don't have any second moment currentOutput, implicitly already folding it in, so we just use the mean squared @@ -983,7 +1031,11 @@ void NeuralNet::getOutput( NNResultBuf** inputBufs, vector& outputs) { - MetalProcess::getMetalOutput(gpuHandle, inputBuffers, numBatchEltsFilled, inputBufs, outputs); + if (gpuHandle->metalhandle) { + MetalProcess::getMetalOutput(gpuHandle, inputBuffers, numBatchEltsFilled, inputBufs, outputs); + } else { + CoreMLProcess::getCoreMLOutput(gpuHandle, inputBuffers, numBatchEltsFilled, inputBufs, outputs); + } } bool MetalProcess::testEvaluateConv(const ConvLayerDesc* desc, @@ -1199,4 +1251,4 @@ bool NeuralNet::testEvaluateGlobalPoolingResidualBlock( return MetalProcess::testEvaluateGlobalPoolingResidualBlock(desc, batchSize, nnXLen, nnYLen, inputBuffer, maskBuffer, outputBuffer); } -#endif // USE_METAL_BACKEND +#endif // USE_COREML_BACKEND diff --git a/cpp/neuralnet/metalbackend.h b/cpp/neuralnet/metalbackend.h index 34e44b8e7..000bfd8c5 100644 --- a/cpp/neuralnet/metalbackend.h +++ b/cpp/neuralnet/metalbackend.h @@ -108,6 +108,11 @@ struct LoadedModel { */ ModelDesc modelDesc; + /** + * @brief The directory of the loaded model. + */ + const string modelDirectory; + /** * @brief Construct a new Loaded Model object * This constructor loads a machine learning model from a file and sets the modelDesc field to the @@ -115,7 +120,8 @@ struct LoadedModel { * @param fileName The name of the file containing the machine learning model. * @param expectedSha256 The expected SHA-256 hash of the model file. */ - LoadedModel(const string& fileName, const string& expectedSha256) + LoadedModel(const string& fileName, const string& expectedSha256, const string& dirName) + :modelDirectory(dirName) { ModelDesc::loadFromFileMaybeGZipped(fileName, modelDesc, expectedSha256); } @@ -153,6 +159,11 @@ struct ComputeContext { */ enabled_t useFP16Mode; + /** + * @brief Whether to use CPU and Neural Engine for CoreML computations. + */ + bool useCpuAndNeuralEngine; + /** * @brief ComputeContext ID */ @@ -171,8 +182,9 @@ struct ComputeContext { * @param nnY The height of the input tensor. * @param useFP16Mode Whether to use half-precision floating-point (FP16) mode for computations. * @param useNHWCMode Whether to use the NHWC format for input tensors. + * @param useCpuAndNeuralEngine Whether to use CPU and Neural Engine for CoreML computations. */ - ComputeContext(int nnX, int nnY, enabled_t useFP16Mode, enabled_t useNHWCMode); + ComputeContext(int nnX, int nnY, enabled_t useFP16Mode, enabled_t useNHWCMode, bool useCpuAndNeuralEngine); /** * @brief Destroys the ComputeContext object. @@ -240,11 +252,36 @@ struct ComputeHandle { */ bool useFP16; + /** + * @brief The x length of the CoreML model. + */ + int modelXLen; + + /** + * @brief The y length of the CoreML model. + */ + int modelYLen; + + /** + * @brief The version of the CoreML model. + */ + int modelVersion; + + /** + * @brief The index of the CoreML model. + */ + int modelIndex; + /** * @brief The Metal handle instance. */ swift::Optional metalhandle; + /** + * @brief The CoreML backend instance. + */ + swift::Optional coremlbackend; + /** * @brief Construct a new ComputeHandle object. * This constructor initializes a new ComputeHandle object with the specified parameters and settings. @@ -286,18 +323,23 @@ struct ComputeHandle { struct InputBuffers { int maxBatchSize; size_t policyResultChannels; + size_t modelPolicyResultChannels; size_t singleSpatialElts; size_t singleInputElts; size_t singleInputGlobalElts; size_t singleInputMetaElts; - size_t singlePolicyResultElts; + size_t singleNnPolicyResultElts; + size_t singleModelPolicyResultElts; size_t singlePolicyPassResultElts; size_t singlePolicyProbsElts; size_t singleValueResultElts; - size_t singleOwnershipResultElts; + size_t singleNnOwnershipResultElts; + size_t singleModelOwnershipResultElts; size_t singleOwnerMapElts; size_t singleScoreValuesResultElts; + size_t singleNnScoreValuesResultElts; + size_t singleMoreMiscValuesResultElts; size_t rowSpatialBufferElts; size_t userInputBufferElts; @@ -306,10 +348,12 @@ struct InputBuffers { size_t policyResultBufferElts; size_t policyPassResultBufferElts; size_t policyProbsBufferElts; + size_t modelPolicyResultBufferElts; size_t valueResultBufferElts; size_t ownershipResultBufferElts; size_t ownerMapBufferElts; size_t scoreValuesResultBufferElts; + size_t moreMiscValuesResultsBufferElts; float* rowSpatialBuffer; float* userInputBuffer; @@ -318,10 +362,12 @@ struct InputBuffers { float* policyResults; float* policyPassResults; float* policyProbsBuffer; + float* modelPolicyResults; float* valueResults; float* ownershipResults; float* ownerMapBuffer; float* scoreValuesResults; + float* moreMiscValuesResults; InputBuffers(const LoadedModel* loadedModel, int maxBatchSz, int nnXLen, int nnYLen); ~InputBuffers(); diff --git a/cpp/neuralnet/metalbackend.swift b/cpp/neuralnet/metalbackend.swift index 97c6e181d..34e77f4b4 100644 --- a/cpp/neuralnet/metalbackend.swift +++ b/cpp/neuralnet/metalbackend.swift @@ -669,6 +669,24 @@ public struct SWBatchNormLayerDesc { let mergedScale: UnsafeMutablePointer let mergedBias: UnsafeMutablePointer + static func mergeScales(scaleWeights: [Float], varianceWeights: [Float], epsilon: Float) -> [Float] { + assert(scaleWeights.count == varianceWeights.count) + + return zip(scaleWeights, varianceWeights).map { scale, variance in + scale / sqrt(variance + epsilon) + } + } + + static func mergedBiases(biasWeights: [Float], meanWeights: [Float], mergedScales: [Float]) -> [Float] { + assert(biasWeights.count == meanWeights.count) + assert(biasWeights.count == mergedScales.count) + + return zip(zip(biasWeights, meanWeights), mergedScales).map { (biasMean, scale) in + let (bias, mean) = biasMean + return bias - (mean * scale) + } + } + /// Initializes a SWBatchNormLayerDesc object. /// - Parameters: /// - numChannels: The number of channels in the input tensor. diff --git a/cpp/neuralnet/metalbridge.h b/cpp/neuralnet/metalbridge.h new file mode 100644 index 000000000..efef3b069 --- /dev/null +++ b/cpp/neuralnet/metalbridge.h @@ -0,0 +1,3 @@ +#ifdef OS_IS_IOS +#import "KataGoHelper.h" +#endif diff --git a/cpp/neuralnet/nneval.cpp b/cpp/neuralnet/nneval.cpp index 595fe78dc..293e45886 100644 --- a/cpp/neuralnet/nneval.cpp +++ b/cpp/neuralnet/nneval.cpp @@ -67,9 +67,62 @@ NNEvaluator::NNEvaluator( const string& rSeed, bool doRandomize, int defaultSymmetry +) + :NNEvaluator( + mName, + mFileName, + "", + expectedSha256, + lg, + maxBatchSz, + xLen, + yLen, + rExactNNLen, + iUseNHWC, + nnCacheSizePowerOfTwo, + nnMutexPoolSizePowerofTwo, + skipNeuralNet, + openCLTunerFile, + homeDataDirOverride, + openCLReTunePerBoardSize, + useFP16Mode, + useNHWCMode, + numThr, + gpuIdxByServerThr, + rSeed, + doRandomize, + defaultSymmetry) +{ +} + +NNEvaluator::NNEvaluator( + const string& mName, + const string& mFileName, + const string& mDirName, + const string& expectedSha256, + Logger* lg, + int maxBatchSz, + int xLen, + int yLen, + bool rExactNNLen, + bool iUseNHWC, + int nnCacheSizePowerOfTwo, + int nnMutexPoolSizePowerofTwo, + bool skipNeuralNet, + const string& openCLTunerFile, + const string& homeDataDirOverride, + bool openCLReTunePerBoardSize, + enabled_t useFP16Mode, + enabled_t useNHWCMode, + int numThr, + const vector& gpuIdxByServerThr, + const string& rSeed, + bool doRandomize, + int defaultSymmetry ) :modelName(mName), modelFileName(mFileName), + modelDirName(mDirName), nnXLen(xLen), nnYLen(yLen), requireExactNNLen(rExactNNLen), @@ -133,7 +186,7 @@ NNEvaluator::NNEvaluator( std::sort(gpuIdxs.begin(), gpuIdxs.end()); auto last = std::unique(gpuIdxs.begin(), gpuIdxs.end()); gpuIdxs.erase(last,gpuIdxs.end()); - loadedModel = NeuralNet::loadModelFile(modelFileName,expectedSha256); + loadedModel = NeuralNet::loadModelFile(modelFileName,expectedSha256,modelDirName); const ModelDesc& desc = NeuralNet::getModelDesc(loadedModel); internalModelName = desc.name; modelVersion = desc.modelVersion; diff --git a/cpp/neuralnet/nneval.h b/cpp/neuralnet/nneval.h index 04ff7506b..d762ceefc 100644 --- a/cpp/neuralnet/nneval.h +++ b/cpp/neuralnet/nneval.h @@ -102,6 +102,31 @@ class NNEvaluator { bool doRandomize, int defaultSymmetry ); + NNEvaluator( + const std::string& modelName, + const std::string& modelFileName, + const std::string& modelDir, + const std::string& expectedSha256, + Logger* logger, + int maxBatchSize, + int nnXLen, + int nnYLen, + bool requireExactNNLen, + bool inputsUseNHWC, + int nnCacheSizePowerOfTwo, + int nnMutexPoolSizePowerofTwo, + bool debugSkipNeuralNet, + const std::string& openCLTunerFile, + const std::string& homeDataDirOverride, + bool openCLReTunePerBoardSize, + enabled_t useFP16Mode, + enabled_t useNHWCMode, + int numThreads, + const std::vector& gpuIdxByServerThread, + const std::string& randSeed, + bool doRandomize, + int defaultSymmetry + ); ~NNEvaluator(); NNEvaluator(const NNEvaluator& other) = delete; @@ -210,6 +235,7 @@ class NNEvaluator { private: const std::string modelName; const std::string modelFileName; + const std::string modelDirName; const int nnXLen; const int nnYLen; const bool requireExactNNLen; diff --git a/cpp/neuralnet/nninterface.h b/cpp/neuralnet/nninterface.h index e5932a6d5..e87b2a35c 100644 --- a/cpp/neuralnet/nninterface.h +++ b/cpp/neuralnet/nninterface.h @@ -39,7 +39,10 @@ namespace NeuralNet { // Model I/O ----------------------------------------------------------------- - LoadedModel* loadModelFile(const std::string& file, const std::string& expectedSha256); + LoadedModel* loadModelFile( + const std::string& file, + const std::string& expectedSha256, + const std::string& dir); void freeLoadedModel(LoadedModel* loadedModel); const ModelDesc& getModelDesc(const LoadedModel* loadedModel); diff --git a/cpp/neuralnet/openclbackend.cpp b/cpp/neuralnet/openclbackend.cpp index c4bcf2728..3cc9a415d 100644 --- a/cpp/neuralnet/openclbackend.cpp +++ b/cpp/neuralnet/openclbackend.cpp @@ -121,8 +121,9 @@ struct LoadedModel { LoadedModel& operator=(const LoadedModel&) = delete; }; -LoadedModel* NeuralNet::loadModelFile(const string& file, const string& expectedSha256) { +LoadedModel* NeuralNet::loadModelFile(const string& file, const string& expectedSha256, const string& dir) { LoadedModel* loadedModel = new LoadedModel(file,expectedSha256); + (void)dir; return loadedModel; } diff --git a/cpp/neuralnet/trtbackend.cpp b/cpp/neuralnet/trtbackend.cpp index c6df9f251..bdfdc9a3c 100644 --- a/cpp/neuralnet/trtbackend.cpp +++ b/cpp/neuralnet/trtbackend.cpp @@ -91,8 +91,9 @@ struct LoadedModel { LoadedModel& operator=(const LoadedModel&) = delete; }; -LoadedModel* NeuralNet::loadModelFile(const string& file, const string& expectedSha256) { +LoadedModel* NeuralNet::loadModelFile(const string& file, const string& expectedSha256, const string& dir) { LoadedModel* loadedModel = new LoadedModel(file, expectedSha256); + (void)dir; return loadedModel; } diff --git a/cpp/program/gtpconfig.cpp b/cpp/program/gtpconfig.cpp index 7a45c02de..08085dd90 100644 --- a/cpp/program/gtpconfig.cpp +++ b/cpp/program/gtpconfig.cpp @@ -535,6 +535,9 @@ string GTPConfig::makeConfig( #endif #ifdef USE_OPENCL_BACKEND replacement += "openclDeviceToUseThread" + Global::intToString(i) + " = " + Global::intToString(deviceIdxs[i]) + "\n"; +#endif +#ifdef USE_COREML_BACKEND + replacement += "coremlDeviceToUseThread" + Global::intToString(i) + " = " + Global::intToString(deviceIdxs[i]) + "\n"; #endif } replace("$$MULTIPLE_GPUS", replacement); diff --git a/cpp/program/setup.cpp b/cpp/program/setup.cpp index e0f6e6ced..6671383e7 100644 --- a/cpp/program/setup.cpp +++ b/cpp/program/setup.cpp @@ -21,6 +21,7 @@ std::vector Setup::getBackendPrefixes() { prefixes.push_back("opencl"); prefixes.push_back("eigen"); prefixes.push_back("dummybackend"); + prefixes.push_back("coreml"); return prefixes; } @@ -38,11 +39,45 @@ NNEvaluator* Setup::initializeNNEvaluator( bool defaultRequireExactNNLen, bool disableFP16, setup_for_t setupFor +) { + return initializeCoreMLEvaluator( + nnModelName, + nnModelFile, + "", + expectedSha256, + cfg, + logger, + seedRand, + expectedConcurrentEvals, + defaultNNXLen, + defaultNNYLen, + defaultMaxBatchSize, + defaultRequireExactNNLen, + disableFP16, + setupFor); +} + +NNEvaluator* Setup::initializeCoreMLEvaluator( + const string& nnModelName, + const string& nnModelFile, + const string& nnModelDir, + const string& expectedSha256, + ConfigParser& cfg, + Logger& logger, + Rand& seedRand, + int expectedConcurrentEvals, + int defaultNNXLen, + int defaultNNYLen, + int defaultMaxBatchSize, + bool defaultRequireExactNNLen, + bool disableFP16, + setup_for_t setupFor ) { vector nnEvals = - initializeNNEvaluators( + initializeCoreMLEvaluators( {nnModelName}, {nnModelFile}, + {nnModelDir}, {expectedSha256}, cfg, logger, @@ -73,9 +108,44 @@ vector Setup::initializeNNEvaluators( bool defaultRequireExactNNLen, bool disableFP16, setup_for_t setupFor +) { + return initializeCoreMLEvaluators( + nnModelNames, + nnModelFiles, + {""}, + expectedSha256s, + cfg, + logger, + seedRand, + expectedConcurrentEvals, + defaultNNXLen, + defaultNNYLen, + defaultMaxBatchSize, + defaultRequireExactNNLen, + disableFP16, + setupFor + ); +} + +vector Setup::initializeCoreMLEvaluators( + const vector& nnModelNames, + const vector& nnModelFiles, + const vector& nnModelDirs, + const vector& expectedSha256s, + ConfigParser& cfg, + Logger& logger, + Rand& seedRand, + int expectedConcurrentEvals, + int defaultNNXLen, + int defaultNNYLen, + int defaultMaxBatchSize, + bool defaultRequireExactNNLen, + bool disableFP16, + setup_for_t setupFor ) { vector nnEvals; assert(nnModelNames.size() == nnModelFiles.size()); + assert(nnModelFiles.size() == nnModelDirs.size()); assert(expectedSha256s.size() == 0 || expectedSha256s.size() == nnModelFiles.size()); #if defined(USE_CUDA_BACKEND) @@ -88,6 +158,8 @@ vector Setup::initializeNNEvaluators( string backendPrefix = "opencl"; #elif defined(USE_EIGEN_BACKEND) string backendPrefix = "eigen"; + #elif defined(USE_COREML_BACKEND) + string backendPrefix = "coreml"; #else string backendPrefix = "dummybackend"; #endif @@ -103,6 +175,7 @@ vector Setup::initializeNNEvaluators( string idxStr = Global::uint64ToString(i); const string& nnModelName = nnModelNames[i]; const string& nnModelFile = nnModelFiles[i]; + const string& nnModelDir = nnModelDirs[i]; const string& expectedSha256 = expectedSha256s.size() > 0 ? expectedSha256s[i]: ""; bool debugSkipNeuralNetDefault = (nnModelFile == "/dev/null"); @@ -141,7 +214,7 @@ vector Setup::initializeNNEvaluators( requireExactNNLen = cfg.getBool("requireMaxBoardSize"); } - bool inputsUseNHWC = backendPrefix == "opencl" || backendPrefix == "trt" || backendPrefix == "metal" ? false : true; + bool inputsUseNHWC = backendPrefix == "opencl" || backendPrefix == "trt" || backendPrefix == "coreml" ? false : true; if(cfg.contains(backendPrefix+"InputsUseNHWC"+idxStr)) inputsUseNHWC = cfg.getBool(backendPrefix+"InputsUseNHWC"+idxStr); else if(cfg.contains("inputsUseNHWC"+idxStr)) @@ -305,6 +378,7 @@ vector Setup::initializeNNEvaluators( NNEvaluator* nnEval = new NNEvaluator( nnModelName, nnModelFile, + nnModelDir, expectedSha256, &logger, nnMaxBatchSize, diff --git a/cpp/program/setup.h b/cpp/program/setup.h index 64d89e3ee..a7db25061 100644 --- a/cpp/program/setup.h +++ b/cpp/program/setup.h @@ -38,6 +38,23 @@ namespace Setup { setup_for_t setupFor ); + NNEvaluator* initializeCoreMLEvaluator( + const std::string& nnModelNames, + const std::string& nnModelFiles, + const std::string& nnModelDir, + const std::string& expectedSha256, + ConfigParser& cfg, + Logger& logger, + Rand& seedRand, + int expectedConcurrentEvals, + int defaultNNXLen, + int defaultNNYLen, + int defaultMaxBatchSize, + bool defaultRequireExactNNLen, + bool disableFP16, + setup_for_t setupFor + ); + std::vector initializeNNEvaluators( const std::vector& nnModelNames, const std::vector& nnModelFiles, @@ -54,6 +71,23 @@ namespace Setup { setup_for_t setupFor ); + std::vector initializeCoreMLEvaluators( + const std::vector& nnModelNames, + const std::vector& nnModelFiles, + const std::vector& nnModelDirs, + const std::vector& expectedSha256s, + ConfigParser& cfg, + Logger& logger, + Rand& seedRand, + int expectedConcurrentEvals, + int defaultNNXLen, + int defaultNNYLen, + int defaultMaxBatchSize, + bool defaultRequireExactNNLen, + bool disableFP16, + setup_for_t setupFor + ); + constexpr int MAX_BOT_PARAMS_FROM_CFG = 4096; constexpr double DEFAULT_ANALYSIS_WIDE_ROOT_NOISE = 0.04; diff --git a/cpp/xcode/KataGo.xcodeproj/project.pbxproj b/cpp/xcode/KataGo.xcodeproj/project.pbxproj new file mode 100644 index 000000000..49f3de422 --- /dev/null +++ b/cpp/xcode/KataGo.xcodeproj/project.pbxproj @@ -0,0 +1,2085 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + E10ACA7D2928A6D30004AB17 /* book.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 973B04213D1B4030B35FB01C /* book.cpp */; }; + E10ACA7E2928A6D30004AB17 /* bookcssjs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6DD28F2EE5FB490F906D63BA /* bookcssjs.cpp */; }; + E10ACA7F2928A6D30004AB17 /* analysis.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E7B41A9FE4124FA1AB3FBEF1 /* analysis.cpp */; }; + E10ACA802928A6D30004AB17 /* benchmark.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 063E4C878E7E43858A863A78 /* benchmark.cpp */; }; + E10ACA812928A6D30004AB17 /* commandline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6CD97C1775DC4E678823595E /* commandline.cpp */; }; + E10ACA822928A6D30004AB17 /* contribute.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D49AE95F1DD947B5BFF58C1F /* contribute.cpp */; }; + E10ACA832928A6D30004AB17 /* evalsgf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CA66CE9038574A0BB16D80B6 /* evalsgf.cpp */; }; + E10ACA842928A6D30004AB17 /* gatekeeper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D8710CF2CCA3478EB65063C6 /* gatekeeper.cpp */; }; + E10ACA862928A6D30004AB17 /* genbook.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2460699580B49F689D028D5 /* genbook.cpp */; }; + E10ACA872928A6D30004AB17 /* gtp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD94201E380643C3985E9D62 /* gtp.cpp */; }; + E10ACA882928A6D30004AB17 /* match.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 948AF9E88374487D85E846C2 /* match.cpp */; }; + E10ACA8A2928A6D30004AB17 /* misc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 64D3C3432AB3409C942F7A0E /* misc.cpp */; }; + E10ACA8B2928A6D30004AB17 /* runtests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5902EDD2F6A74BE7966E2001 /* runtests.cpp */; }; + E10ACA8C2928A6D30004AB17 /* sandbox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 11318DB744F340DCB41F7248 /* sandbox.cpp */; }; + E10ACA8D2928A6D30004AB17 /* selfplay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFF33AEBABB1472B9F241A98 /* selfplay.cpp */; }; + E10ACA8E2928A6D30004AB17 /* tune.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A241D7415C384D3A81BF73AC /* tune.cpp */; }; + E10ACA8F2928A6D30004AB17 /* base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D61629242F5143EBB2D9BEC9 /* base64.cpp */; }; + E10ACA902928A6D30004AB17 /* bsearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 176C18FD215D45179B93393C /* bsearch.cpp */; }; + E10ACA912928A6D30004AB17 /* commandloop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF5823DCA854224809D93A8 /* commandloop.cpp */; }; + E10ACA922928A6D30004AB17 /* config_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23D034621365403182419780 /* config_parser.cpp */; }; + E10ACA932928A6D30004AB17 /* datetime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71DC745C32B543C191262823 /* datetime.cpp */; }; + E10ACA942928A6D30004AB17 /* elo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 59353ECA2B0140FA9365623E /* elo.cpp */; }; + E10ACA952928A6D30004AB17 /* fancymath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2626105D31ED44D98E6B9B9D /* fancymath.cpp */; }; + E10ACA962928A6D30004AB17 /* fileutils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CAD1B260FFB74AF9BA66A58A /* fileutils.cpp */; }; + E10ACA972928A6D30004AB17 /* global.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A8748F2EFAAF401DACE6B60A /* global.cpp */; }; + E10ACA982928A6D30004AB17 /* hash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BDF52FD481AA424BBC59124D /* hash.cpp */; }; + E10ACA992928A6D30004AB17 /* logger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7B2C186FF8B3422CB64E6039 /* logger.cpp */; }; + E10ACA9A2928A6D30004AB17 /* mainargs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 92F4695F66A84118BDCAA13F /* mainargs.cpp */; }; + E10ACA9B2928A6D30004AB17 /* makedir.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 63D5831B449B48D1AD132F9F /* makedir.cpp */; }; + E10ACA9C2928A6D30004AB17 /* md5.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE7F7520CA15440EBDF0A21D /* md5.cpp */; }; + E10ACA9D2928A6D30004AB17 /* multithread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5185F4BC63B5490AAE4F37CB /* multithread.cpp */; }; + E10ACA9E2928A6D30004AB17 /* rand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B8E283A3B8004F289DACCD8A /* rand.cpp */; }; + E10ACA9F2928A6D30004AB17 /* rand_helpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 59BC63FBF0804F63A27369AE /* rand_helpers.cpp */; }; + E10ACAA02928A6D30004AB17 /* sha2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76F8951F199F416F99B96FE8 /* sha2.cpp */; }; + E10ACAA12928A6D30004AB17 /* test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5639F08A96FD467CBD091947 /* test.cpp */; }; + E10ACAA22928A6D30004AB17 /* threadsafecounter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D645BB8AAF424700A75ED223 /* threadsafecounter.cpp */; }; + E10ACAA32928A6D30004AB17 /* threadsafequeue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34B63C891D53453F9C258280 /* threadsafequeue.cpp */; }; + E10ACAA42928A6D30004AB17 /* threadtest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69300B311DE94520A56A3B5F /* threadtest.cpp */; }; + E10ACAA52928A6D30004AB17 /* timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EEB543E9A42948748BF883C3 /* timer.cpp */; }; + E10ACAA62928A6D30004AB17 /* files.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C31483CD76D48F2A7327613 /* files.cpp */; }; + E10ACAA72928A6D30004AB17 /* homedata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6E87CD61EFA340A1AF4B8BCE /* homedata.cpp */; }; + E10ACAA82928A6D30004AB17 /* loadmodel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8FBE5F0F301A405D85F23D38 /* loadmodel.cpp */; }; + E10ACAA92928A6D30004AB17 /* numpywrite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F20754875D24724A133A9AE /* numpywrite.cpp */; }; + E10ACAAA2928A6D30004AB17 /* sgf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3E097292E4F34AB6806F67E6 /* sgf.cpp */; }; + E10ACAAB2928A6D30004AB17 /* trainingwrite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6F9788817DEA4417A321C3A0 /* trainingwrite.cpp */; }; + E10ACAAC2928A6D30004AB17 /* client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 792CF6207CA54AABB0F058C6 /* client.cpp */; }; + E10ACAAD2928A6D30004AB17 /* board.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8F0B49CAFCB24D31808DB2C1 /* board.cpp */; }; + E10ACAAE2928A6D30004AB17 /* boardhistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 540D93E0576C47C789279AF8 /* boardhistory.cpp */; }; + E10ACAAF2928A6D30004AB17 /* graphhash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 10EB7D2538F94B26BE1B1740 /* graphhash.cpp */; }; + E10ACAB02928A6D30004AB17 /* rules.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 727A790F2FEA4DBEA8ABAE85 /* rules.cpp */; }; + E10ACAB12928A6D30004AB17 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 50827347EBFE4467996C3150 /* main.cpp */; }; + E10ACAB22928A6D30004AB17 /* desc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5D8F26726AAF403C833FBD7F /* desc.cpp */; }; + E10ACAB32928A6D30004AB17 /* metalbackend.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4845ACCEFC204BA89C033482 /* metalbackend.cpp */; }; + E10ACAB52928A6D30004AB17 /* modelversion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DDCAE99038794BE8B4BB3962 /* modelversion.cpp */; }; + E10ACAB62928A6D30004AB17 /* nneval.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 92C3AF4C79ED491988E9C5BC /* nneval.cpp */; }; + E10ACAB72928A6D30004AB17 /* nninputs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D41000BDB70543A4820D445A /* nninputs.cpp */; }; + E10ACAB82928A6D30004AB17 /* gtpconfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5BCE97296A5249A0B49C766F /* gtpconfig.cpp */; }; + E10ACAB92928A6D30004AB17 /* play.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FBACE432776421CAEDF6786 /* play.cpp */; }; + E10ACABA2928A6D30004AB17 /* playsettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A57BA046921422DB33C7614 /* playsettings.cpp */; }; + E10ACABB2928A6D30004AB17 /* playutils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9FB3A34B1C8D4CBF9997DDA7 /* playutils.cpp */; }; + E10ACABC2928A6D30004AB17 /* selfplaymanager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C7A65C82B4C4AB5B83B1346 /* selfplaymanager.cpp */; }; + E10ACABD2928A6D30004AB17 /* setup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D104762E63AF4C6A8ADB220E /* setup.cpp */; }; + E10ACABE2928A6D30004AB17 /* analysisdata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BF423768A6B74FF18FDC44E7 /* analysisdata.cpp */; }; + E10ACABF2928A6D30004AB17 /* asyncbot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F2D4BF5BF0CD446F80DFDACE /* asyncbot.cpp */; }; + E10ACAC02928A6D30004AB17 /* distributiontable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 32DD1B600C014B49ADDB237E /* distributiontable.cpp */; }; + E10ACAC12928A6D30004AB17 /* localpattern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD4302F4D69E4EE98EA75B2C /* localpattern.cpp */; }; + E10ACAC22928A6D30004AB17 /* mutexpool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6DA721BDC00F438688E0B241 /* mutexpool.cpp */; }; + E10ACAC32928A6D30004AB17 /* patternbonustable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6A5C095FD31A4636994B5E5A /* patternbonustable.cpp */; }; + E10ACAC42928A6D30004AB17 /* reportedsearchvalues.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 706365E669744784A6A6DE57 /* reportedsearchvalues.cpp */; }; + E10ACAC52928A6D30004AB17 /* search.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 93FF01FEC8DA40DB916C4F0A /* search.cpp */; }; + E10ACAC62928A6D30004AB17 /* searchexplorehelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EC59266A435045C5B84F9105 /* searchexplorehelpers.cpp */; }; + E10ACAC72928A6D30004AB17 /* searchhelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A72EC47D68904D38A5EAE635 /* searchhelpers.cpp */; }; + E10ACAC82928A6D30004AB17 /* searchmirror.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07DAAE05A9FA46F5B271903E /* searchmirror.cpp */; }; + E10ACAC92928A6D30004AB17 /* searchmultithreadhelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BCBCE4A8D83F42FBA4EA0CBE /* searchmultithreadhelpers.cpp */; }; + E10ACACA2928A6D30004AB17 /* searchnnhelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA6C3E7D4604497D8B94AC50 /* searchnnhelpers.cpp */; }; + E10ACACB2928A6D30004AB17 /* searchnode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 206727F6853C468F84FC44AE /* searchnode.cpp */; }; + E10ACACC2928A6D30004AB17 /* searchnodetable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C33571C53ECC4C82B0A9DA7D /* searchnodetable.cpp */; }; + E10ACACD2928A6D30004AB17 /* searchparams.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1660F43339464F1F82D603C2 /* searchparams.cpp */; }; + E10ACACE2928A6D30004AB17 /* searchresults.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1BAD528CE45E4D31A6F0F058 /* searchresults.cpp */; }; + E10ACACF2928A6D30004AB17 /* searchtimehelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 77C31BA9C8864C07B491DF1D /* searchtimehelpers.cpp */; }; + E10ACAD02928A6D30004AB17 /* searchupdatehelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 73D2A262E3E542FD8063F8DD /* searchupdatehelpers.cpp */; }; + E10ACAD12928A6D30004AB17 /* subtreevaluebiastable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7891834D8FB144E0B13F6E21 /* subtreevaluebiastable.cpp */; }; + E10ACAD22928A6D30004AB17 /* timecontrols.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 888C7B98F8B64150B0903946 /* timecontrols.cpp */; }; + E10ACAD32928A6D30004AB17 /* testboardarea.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D4E9B8ABFBF4DAEB11058E1 /* testboardarea.cpp */; }; + E10ACAD42928A6D30004AB17 /* testboardbasic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F18310A722494DAEACBE09BC /* testboardbasic.cpp */; }; + E10ACAD52928A6D30004AB17 /* testcommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C9D17518AE04398A975E5AE /* testcommon.cpp */; }; + E10ACAD62928A6D30004AB17 /* testconfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 346C96C8324D4BE8A12D1A97 /* testconfig.cpp */; }; + E10ACAD72928A6D30004AB17 /* testmisc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 48669007B9164F5FB011F549 /* testmisc.cpp */; }; + E10ACAD82928A6D30004AB17 /* testnn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41CCB0DF860045E5A8697BDD /* testnn.cpp */; }; + E10ACAD92928A6D30004AB17 /* testnnevalcanary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 88BAF51D4B34475A90D1D7CC /* testnnevalcanary.cpp */; }; + E10ACADA2928A6D30004AB17 /* testnninputs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B137CD979C7436188D684A7 /* testnninputs.cpp */; }; + E10ACADB2928A6D30004AB17 /* testownership.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F91005809465EB2EDD409 /* testownership.cpp */; }; + E10ACADC2928A6D30004AB17 /* testrules.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2F5B917DA90147ABBAC18571 /* testrules.cpp */; }; + E10ACADD2928A6D30004AB17 /* testscore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3F8D82F94E14F11BA0F59E6 /* testscore.cpp */; }; + E10ACADE2928A6D30004AB17 /* testsearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2F9938E72849F691272AA0 /* testsearch.cpp */; }; + E10ACADF2928A6D30004AB17 /* testsearchcommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0EDC97A2834E434691EA91C1 /* testsearchcommon.cpp */; }; + E10ACAE02928A6D30004AB17 /* testsearchmisc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF2B81FB1BB43AC81344E4A /* testsearchmisc.cpp */; }; + E10ACAE12928A6D30004AB17 /* testsearchnonn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BC9F65190B644C969D327CD9 /* testsearchnonn.cpp */; }; + E10ACAE22928A6D30004AB17 /* testsearchv3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 43CF521030274453B04827E1 /* testsearchv3.cpp */; }; + E10ACAE32928A6D30004AB17 /* testsearchv8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 661A920818694712953495A7 /* testsearchv8.cpp */; }; + E10ACAE42928A6D30004AB17 /* testsearchv9.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1356448A03004176848C790A /* testsearchv9.cpp */; }; + E10ACAE52928A6D30004AB17 /* testsgf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952F0B54C8BF410C9EA67989 /* testsgf.cpp */; }; + E10ACAE62928A6D30004AB17 /* testsymmetries.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84BCAFD2361F4BE8B5025F65 /* testsymmetries.cpp */; }; + E10ACAE72928A6D30004AB17 /* testtime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A255C9FAA2E145048F33368C /* testtime.cpp */; }; + E10ACAE82928A6D30004AB17 /* testtrainingwrite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D1DFBE2386CE449D82894520 /* testtrainingwrite.cpp */; }; + E10ACAE92928A6D30004AB17 /* tinymodel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE70F73F685D4EDA9977822F /* tinymodel.cpp */; }; + E10ACAEA2928A6D30004AB17 /* tinymodeldata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 279C4ABB40FE447483F0F975 /* tinymodeldata.cpp */; }; + E10ACAEC2928A6D30004AB17 /* MetalPerformanceShaders.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E1AD404A28E1D59700E41968 /* MetalPerformanceShaders.framework */; }; + E10ACAED2928A6D30004AB17 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E1AD405128E1D75B00E41968 /* libz.tbd */; }; + E10ACAEE2928A6D30004AB17 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E1AD404928E1D59700E41968 /* Metal.framework */; }; + E10ACAEF2928A6D30004AB17 /* MetalPerformanceShadersGraph.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E1AD404B28E1D59700E41968 /* MetalPerformanceShadersGraph.framework */; }; + E10ACAFA2928A8D30004AB17 /* coremlbackend.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E13CF66228E1896C005CB016 /* coremlbackend.cpp */; }; + E10ACAFD2928BBF00004AB17 /* CoreML.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E1AD404F28E1D5A700E41968 /* CoreML.framework */; }; + E12453D52A1CF0DE0062DF9C /* testbook.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E12453D42A1CF0DE0062DF9C /* testbook.cpp */; }; + E12453D72A1D015E0062DF9C /* poswriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E12453D62A1D015E0062DF9C /* poswriter.cpp */; }; + E12EC21A2B10D61E0024E274 /* coremlbackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12EC2172B10D61E0024E274 /* coremlbackend.swift */; }; + E12EC21B2B10D61E0024E274 /* coremlbackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12EC2172B10D61E0024E274 /* coremlbackend.swift */; }; + E12EC21C2B10D61E0024E274 /* metalbackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12EC2182B10D61E0024E274 /* metalbackend.swift */; }; + E12EC21D2B10D61E0024E274 /* metalbackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12EC2182B10D61E0024E274 /* metalbackend.swift */; }; + E12EC21E2B10D61E0024E274 /* coremlmodel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12EC2192B10D61E0024E274 /* coremlmodel.swift */; }; + E12EC21F2B10D61E0024E274 /* coremlmodel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12EC2192B10D61E0024E274 /* coremlmodel.swift */; }; + E12EC22E2B10E3310024E274 /* KataGoSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E1DACF4C2B08997300082FF7 /* KataGoSwift.framework */; }; + E12EC2302B1237440024E274 /* parallel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E12EC22F2B1237440024E274 /* parallel.cpp */; }; + E12EC2312B1237440024E274 /* parallel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E12EC22F2B1237440024E274 /* parallel.cpp */; }; + E12EC2332B12375C0024E274 /* writetrainingdata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E12EC2322B12375B0024E274 /* writetrainingdata.cpp */; }; + E12EC2342B12375C0024E274 /* writetrainingdata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E12EC2322B12375B0024E274 /* writetrainingdata.cpp */; }; + E157FDD82AF7D1E500E25677 /* analysis.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E7B41A9FE4124FA1AB3FBEF1 /* analysis.cpp */; }; + E157FDD92AF7D1E500E25677 /* analysisdata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BF423768A6B74FF18FDC44E7 /* analysisdata.cpp */; }; + E157FDDA2AF7D1E500E25677 /* asyncbot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F2D4BF5BF0CD446F80DFDACE /* asyncbot.cpp */; }; + E157FDDB2AF7D1E500E25677 /* base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D61629242F5143EBB2D9BEC9 /* base64.cpp */; }; + E157FDDC2AF7D1E500E25677 /* benchmark.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 063E4C878E7E43858A863A78 /* benchmark.cpp */; }; + E157FDDD2AF7D1E500E25677 /* board.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8F0B49CAFCB24D31808DB2C1 /* board.cpp */; }; + E157FDDE2AF7D1E500E25677 /* boardhistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 540D93E0576C47C789279AF8 /* boardhistory.cpp */; }; + E157FDDF2AF7D1E500E25677 /* book.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 973B04213D1B4030B35FB01C /* book.cpp */; }; + E157FDE02AF7D1E500E25677 /* bookcssjs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6DD28F2EE5FB490F906D63BA /* bookcssjs.cpp */; }; + E157FDE12AF7D1E500E25677 /* bsearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 176C18FD215D45179B93393C /* bsearch.cpp */; }; + E157FDE22AF7D1E500E25677 /* client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 792CF6207CA54AABB0F058C6 /* client.cpp */; }; + E157FDE32AF7D1E500E25677 /* commandline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6CD97C1775DC4E678823595E /* commandline.cpp */; }; + E157FDE42AF7D1E500E25677 /* commandloop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF5823DCA854224809D93A8 /* commandloop.cpp */; }; + E157FDE52AF7D1E600E25677 /* config_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23D034621365403182419780 /* config_parser.cpp */; }; + E157FDE62AF7D1E600E25677 /* contribute.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D49AE95F1DD947B5BFF58C1F /* contribute.cpp */; }; + E157FDE72AF7D1E600E25677 /* coremlbackend.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E13CF66228E1896C005CB016 /* coremlbackend.cpp */; }; + E157FDEA2AF7D1E600E25677 /* datetime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71DC745C32B543C191262823 /* datetime.cpp */; }; + E157FDEB2AF7D1E600E25677 /* desc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5D8F26726AAF403C833FBD7F /* desc.cpp */; }; + E157FDEC2AF7D1E600E25677 /* distributiontable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 32DD1B600C014B49ADDB237E /* distributiontable.cpp */; }; + E157FDED2AF7D1E600E25677 /* elo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 59353ECA2B0140FA9365623E /* elo.cpp */; }; + E157FDEE2AF7D1E600E25677 /* evalsgf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CA66CE9038574A0BB16D80B6 /* evalsgf.cpp */; }; + E157FDEF2AF7D1E600E25677 /* fancymath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2626105D31ED44D98E6B9B9D /* fancymath.cpp */; }; + E157FDF02AF7D1E600E25677 /* files.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C31483CD76D48F2A7327613 /* files.cpp */; }; + E157FDF12AF7D1E600E25677 /* fileutils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CAD1B260FFB74AF9BA66A58A /* fileutils.cpp */; }; + E157FDF22AF7D1E600E25677 /* gatekeeper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D8710CF2CCA3478EB65063C6 /* gatekeeper.cpp */; }; + E157FDF32AF7D1E600E25677 /* genbook.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2460699580B49F689D028D5 /* genbook.cpp */; }; + E157FDF42AF7D1E600E25677 /* global.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A8748F2EFAAF401DACE6B60A /* global.cpp */; }; + E157FDF52AF7D1E600E25677 /* gputest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E17D098A294D45CF005968E9 /* gputest.cpp */; }; + E157FDF62AF7D1E600E25677 /* graphhash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 10EB7D2538F94B26BE1B1740 /* graphhash.cpp */; }; + E157FDF72AF7D1E600E25677 /* gtp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD94201E380643C3985E9D62 /* gtp.cpp */; }; + E157FDF82AF7D1E600E25677 /* gtpconfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5BCE97296A5249A0B49C766F /* gtpconfig.cpp */; }; + E157FDF92AF7D1E600E25677 /* hash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BDF52FD481AA424BBC59124D /* hash.cpp */; }; + E157FDFA2AF7D1E600E25677 /* homedata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6E87CD61EFA340A1AF4B8BCE /* homedata.cpp */; }; + E157FDFB2AF7D1E600E25677 /* loadmodel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8FBE5F0F301A405D85F23D38 /* loadmodel.cpp */; }; + E157FDFC2AF7D1E600E25677 /* localpattern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD4302F4D69E4EE98EA75B2C /* localpattern.cpp */; }; + E157FDFD2AF7D1E600E25677 /* logger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7B2C186FF8B3422CB64E6039 /* logger.cpp */; }; + E157FDFE2AF7D1E600E25677 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 50827347EBFE4467996C3150 /* main.cpp */; }; + E157FDFF2AF7D1E600E25677 /* mainargs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 92F4695F66A84118BDCAA13F /* mainargs.cpp */; }; + E157FE002AF7D1E600E25677 /* makedir.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 63D5831B449B48D1AD132F9F /* makedir.cpp */; }; + E157FE012AF7D1E600E25677 /* match.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 948AF9E88374487D85E846C2 /* match.cpp */; }; + E157FE022AF7D1E600E25677 /* md5.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE7F7520CA15440EBDF0A21D /* md5.cpp */; }; + E157FE032AF7D1E600E25677 /* metalbackend.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4845ACCEFC204BA89C033482 /* metalbackend.cpp */; }; + E157FE052AF7D1E600E25677 /* misc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 64D3C3432AB3409C942F7A0E /* misc.cpp */; }; + E157FE062AF7D1E600E25677 /* modelversion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DDCAE99038794BE8B4BB3962 /* modelversion.cpp */; }; + E157FE072AF7D1E600E25677 /* multithread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5185F4BC63B5490AAE4F37CB /* multithread.cpp */; }; + E157FE082AF7D1E600E25677 /* mutexpool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6DA721BDC00F438688E0B241 /* mutexpool.cpp */; }; + E157FE092AF7D1E600E25677 /* nneval.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 92C3AF4C79ED491988E9C5BC /* nneval.cpp */; }; + E157FE0A2AF7D1E600E25677 /* nninputs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D41000BDB70543A4820D445A /* nninputs.cpp */; }; + E157FE0B2AF7D1E600E25677 /* numpywrite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F20754875D24724A133A9AE /* numpywrite.cpp */; }; + E157FE0C2AF7D1E600E25677 /* patternbonustable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6A5C095FD31A4636994B5E5A /* patternbonustable.cpp */; }; + E157FE0D2AF7D1E600E25677 /* play.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FBACE432776421CAEDF6786 /* play.cpp */; }; + E157FE0E2AF7D1E600E25677 /* playsettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A57BA046921422DB33C7614 /* playsettings.cpp */; }; + E157FE0F2AF7D1E600E25677 /* playutils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9FB3A34B1C8D4CBF9997DDA7 /* playutils.cpp */; }; + E157FE102AF7D1E600E25677 /* poswriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E12453D62A1D015E0062DF9C /* poswriter.cpp */; }; + E157FE112AF7D1E600E25677 /* rand_helpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 59BC63FBF0804F63A27369AE /* rand_helpers.cpp */; }; + E157FE122AF7D1E600E25677 /* rand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B8E283A3B8004F289DACCD8A /* rand.cpp */; }; + E157FE132AF7D1E600E25677 /* reportedsearchvalues.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 706365E669744784A6A6DE57 /* reportedsearchvalues.cpp */; }; + E157FE142AF7D1E600E25677 /* rules.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 727A790F2FEA4DBEA8ABAE85 /* rules.cpp */; }; + E157FE152AF7D1E600E25677 /* runtests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5902EDD2F6A74BE7966E2001 /* runtests.cpp */; }; + E157FE162AF7D1E600E25677 /* sandbox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 11318DB744F340DCB41F7248 /* sandbox.cpp */; }; + E157FE172AF7D1E600E25677 /* search.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 93FF01FEC8DA40DB916C4F0A /* search.cpp */; }; + E157FE182AF7D1E600E25677 /* searchexplorehelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EC59266A435045C5B84F9105 /* searchexplorehelpers.cpp */; }; + E157FE192AF7D1E600E25677 /* searchhelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A72EC47D68904D38A5EAE635 /* searchhelpers.cpp */; }; + E157FE1A2AF7D1E600E25677 /* searchmirror.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07DAAE05A9FA46F5B271903E /* searchmirror.cpp */; }; + E157FE1B2AF7D1E600E25677 /* searchmultithreadhelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BCBCE4A8D83F42FBA4EA0CBE /* searchmultithreadhelpers.cpp */; }; + E157FE1C2AF7D1E600E25677 /* searchnnhelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AA6C3E7D4604497D8B94AC50 /* searchnnhelpers.cpp */; }; + E157FE1D2AF7D1E600E25677 /* searchnode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 206727F6853C468F84FC44AE /* searchnode.cpp */; }; + E157FE1E2AF7D1E600E25677 /* searchnodetable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C33571C53ECC4C82B0A9DA7D /* searchnodetable.cpp */; }; + E157FE1F2AF7D1E600E25677 /* searchparams.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1660F43339464F1F82D603C2 /* searchparams.cpp */; }; + E157FE202AF7D1E600E25677 /* searchresults.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1BAD528CE45E4D31A6F0F058 /* searchresults.cpp */; }; + E157FE212AF7D1E600E25677 /* searchtimehelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 77C31BA9C8864C07B491DF1D /* searchtimehelpers.cpp */; }; + E157FE222AF7D1E600E25677 /* searchupdatehelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 73D2A262E3E542FD8063F8DD /* searchupdatehelpers.cpp */; }; + E157FE232AF7D1E600E25677 /* selfplay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFF33AEBABB1472B9F241A98 /* selfplay.cpp */; }; + E157FE242AF7D1E600E25677 /* selfplaymanager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C7A65C82B4C4AB5B83B1346 /* selfplaymanager.cpp */; }; + E157FE252AF7D1E600E25677 /* setup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D104762E63AF4C6A8ADB220E /* setup.cpp */; }; + E157FE262AF7D1E600E25677 /* sgf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3E097292E4F34AB6806F67E6 /* sgf.cpp */; }; + E157FE272AF7D1E600E25677 /* sha2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76F8951F199F416F99B96FE8 /* sha2.cpp */; }; + E157FE282AF7D1E600E25677 /* subtreevaluebiastable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7891834D8FB144E0B13F6E21 /* subtreevaluebiastable.cpp */; }; + E157FE292AF7D1E600E25677 /* test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5639F08A96FD467CBD091947 /* test.cpp */; }; + E157FE2A2AF7D1E600E25677 /* testboardarea.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D4E9B8ABFBF4DAEB11058E1 /* testboardarea.cpp */; }; + E157FE2B2AF7D1E600E25677 /* testboardbasic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F18310A722494DAEACBE09BC /* testboardbasic.cpp */; }; + E157FE2C2AF7D1E600E25677 /* testbook.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E12453D42A1CF0DE0062DF9C /* testbook.cpp */; }; + E157FE2D2AF7D1E600E25677 /* testcommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C9D17518AE04398A975E5AE /* testcommon.cpp */; }; + E157FE2E2AF7D1E600E25677 /* testconfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 346C96C8324D4BE8A12D1A97 /* testconfig.cpp */; }; + E157FE2F2AF7D1E600E25677 /* testmisc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 48669007B9164F5FB011F549 /* testmisc.cpp */; }; + E157FE302AF7D1E600E25677 /* testnn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41CCB0DF860045E5A8697BDD /* testnn.cpp */; }; + E157FE312AF7D1E600E25677 /* testnnevalcanary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 88BAF51D4B34475A90D1D7CC /* testnnevalcanary.cpp */; }; + E157FE322AF7D1E700E25677 /* testnninputs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B137CD979C7436188D684A7 /* testnninputs.cpp */; }; + E157FE332AF7D1E700E25677 /* testownership.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F91005809465EB2EDD409 /* testownership.cpp */; }; + E157FE342AF7D1E700E25677 /* testrules.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2F5B917DA90147ABBAC18571 /* testrules.cpp */; }; + E157FE352AF7D1E700E25677 /* testscore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3F8D82F94E14F11BA0F59E6 /* testscore.cpp */; }; + E157FE362AF7D1E700E25677 /* testsearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E2F9938E72849F691272AA0 /* testsearch.cpp */; }; + E157FE372AF7D1E700E25677 /* testsearchcommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0EDC97A2834E434691EA91C1 /* testsearchcommon.cpp */; }; + E157FE382AF7D1E700E25677 /* testsearchmisc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF2B81FB1BB43AC81344E4A /* testsearchmisc.cpp */; }; + E157FE392AF7D1E700E25677 /* testsearchnonn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BC9F65190B644C969D327CD9 /* testsearchnonn.cpp */; }; + E157FE3A2AF7D1E700E25677 /* testsearchv3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 43CF521030274453B04827E1 /* testsearchv3.cpp */; }; + E157FE3B2AF7D1E700E25677 /* testsearchv8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 661A920818694712953495A7 /* testsearchv8.cpp */; }; + E157FE3C2AF7D1E700E25677 /* testsearchv9.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1356448A03004176848C790A /* testsearchv9.cpp */; }; + E157FE3D2AF7D1E700E25677 /* testsgf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 952F0B54C8BF410C9EA67989 /* testsgf.cpp */; }; + E157FE3E2AF7D1E700E25677 /* testsymmetries.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84BCAFD2361F4BE8B5025F65 /* testsymmetries.cpp */; }; + E157FE3F2AF7D1E700E25677 /* testtime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A255C9FAA2E145048F33368C /* testtime.cpp */; }; + E157FE402AF7D1E700E25677 /* testtrainingwrite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D1DFBE2386CE449D82894520 /* testtrainingwrite.cpp */; }; + E157FE412AF7D1E700E25677 /* threadsafecounter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D645BB8AAF424700A75ED223 /* threadsafecounter.cpp */; }; + E157FE422AF7D1E700E25677 /* threadsafequeue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34B63C891D53453F9C258280 /* threadsafequeue.cpp */; }; + E157FE432AF7D1E700E25677 /* threadtest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69300B311DE94520A56A3B5F /* threadtest.cpp */; }; + E157FE442AF7D1E700E25677 /* timecontrols.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 888C7B98F8B64150B0903946 /* timecontrols.cpp */; }; + E157FE452AF7D1E700E25677 /* timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EEB543E9A42948748BF883C3 /* timer.cpp */; }; + E157FE462AF7D1E700E25677 /* tinymodel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE70F73F685D4EDA9977822F /* tinymodel.cpp */; }; + E157FE472AF7D1E700E25677 /* tinymodeldata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 279C4ABB40FE447483F0F975 /* tinymodeldata.cpp */; }; + E157FE482AF7D1E700E25677 /* trainingwrite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6F9788817DEA4417A321C3A0 /* trainingwrite.cpp */; }; + E157FE492AF7D1E700E25677 /* tune.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A241D7415C384D3A81BF73AC /* tune.cpp */; }; + E157FE4A2AF7D22800E25677 /* MetalPerformanceShaders.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E1AD404A28E1D59700E41968 /* MetalPerformanceShaders.framework */; }; + E157FE4B2AF7D23800E25677 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E1AD405128E1D75B00E41968 /* libz.tbd */; }; + E157FE4C2AF7D2E400E25677 /* CoreML.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E1AD404F28E1D5A700E41968 /* CoreML.framework */; }; + E157FE4D2AF7D2E800E25677 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E1AD404928E1D59700E41968 /* Metal.framework */; }; + E157FE4E2AF7D2ED00E25677 /* MetalPerformanceShadersGraph.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E1AD404B28E1D59700E41968 /* MetalPerformanceShadersGraph.framework */; }; + E157FE4F2AF7DA1600E25677 /* testnn.mm in Sources */ = {isa = PBXBuildFile; fileRef = E157FDCE2AF7CE2500E25677 /* testnn.mm */; }; + E1605CE22BFAD6EB00A4B872 /* sgfmetadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1605CE12BFAD6EB00A4B872 /* sgfmetadata.cpp */; }; + E1605CE32BFAD70100A4B872 /* sgfmetadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1605CE12BFAD6EB00A4B872 /* sgfmetadata.cpp */; }; + E16BC82D2C4A8AEB00EA3A1E /* ModelTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16BC82C2C4A8AEB00EA3A1E /* ModelTest.swift */; }; + E16BC82F2C4B461500EA3A1E /* CoreMLBackendTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16BC82E2C4B461500EA3A1E /* CoreMLBackendTest.swift */; }; + E16BC8352C4B835F00EA3A1E /* CoreMLModelTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16BC8342C4B835F00EA3A1E /* CoreMLModelTest.swift */; }; + E17D098C294D45CF005968E9 /* gputest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E17D098A294D45CF005968E9 /* gputest.cpp */; }; + E1DACF5D2B089A5400082FF7 /* KataGoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E1DACF4C2B08997300082FF7 /* KataGoSwift.framework */; }; + E1DACF652B089B5500082FF7 /* KataGoSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1DACF642B089B5500082FF7 /* KataGoSwiftTests.swift */; }; + E1DACF732B089C7700082FF7 /* KataGoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E1DACF4C2B08997300082FF7 /* KataGoSwift.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + E1DACF5B2B089A4B00082FF7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 91644CF2108748368B902DCE /* Project object */; + proxyType = 1; + remoteGlobalIDString = E1DACF4B2B08997300082FF7; + remoteInfo = KataGoSwift; + }; + E1DACF712B089C6F00082FF7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 91644CF2108748368B902DCE /* Project object */; + proxyType = 1; + remoteGlobalIDString = E1DACF4B2B08997300082FF7; + remoteInfo = KataGoSwift; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + E12EC22D2B10E3200024E274 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ../Frameworks; + dstSubfolderSpec = 16; + files = ( + E12EC22E2B10E3310024E274 /* KataGoSwift.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 063E4C878E7E43858A863A78 /* benchmark.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; indentWidth = 2; name = benchmark.cpp; path = command/benchmark.cpp; sourceTree = SOURCE_ROOT; }; + 07DAAE05A9FA46F5B271903E /* searchmirror.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = searchmirror.cpp; path = search/searchmirror.cpp; sourceTree = SOURCE_ROOT; }; + 0E2F9938E72849F691272AA0 /* testsearch.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testsearch.cpp; path = tests/testsearch.cpp; sourceTree = SOURCE_ROOT; }; + 0EDC97A2834E434691EA91C1 /* testsearchcommon.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testsearchcommon.cpp; path = tests/testsearchcommon.cpp; sourceTree = SOURCE_ROOT; }; + 0F8F91005809465EB2EDD409 /* testownership.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testownership.cpp; path = tests/testownership.cpp; sourceTree = SOURCE_ROOT; }; + 10EB7D2538F94B26BE1B1740 /* graphhash.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = graphhash.cpp; path = game/graphhash.cpp; sourceTree = SOURCE_ROOT; }; + 11318DB744F340DCB41F7248 /* sandbox.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = sandbox.cpp; path = command/sandbox.cpp; sourceTree = SOURCE_ROOT; }; + 1356448A03004176848C790A /* testsearchv9.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testsearchv9.cpp; path = tests/testsearchv9.cpp; sourceTree = SOURCE_ROOT; }; + 1660F43339464F1F82D603C2 /* searchparams.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = searchparams.cpp; path = search/searchparams.cpp; sourceTree = SOURCE_ROOT; }; + 176C18FD215D45179B93393C /* bsearch.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = bsearch.cpp; path = core/bsearch.cpp; sourceTree = SOURCE_ROOT; }; + 1BAD528CE45E4D31A6F0F058 /* searchresults.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = searchresults.cpp; path = search/searchresults.cpp; sourceTree = SOURCE_ROOT; }; + 206727F6853C468F84FC44AE /* searchnode.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = searchnode.cpp; path = search/searchnode.cpp; sourceTree = SOURCE_ROOT; }; + 23D034621365403182419780 /* config_parser.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = config_parser.cpp; path = core/config_parser.cpp; sourceTree = SOURCE_ROOT; }; + 2626105D31ED44D98E6B9B9D /* fancymath.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = fancymath.cpp; path = core/fancymath.cpp; sourceTree = SOURCE_ROOT; }; + 279C4ABB40FE447483F0F975 /* tinymodeldata.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = tinymodeldata.cpp; path = tests/tinymodeldata.cpp; sourceTree = SOURCE_ROOT; }; + 2F5B917DA90147ABBAC18571 /* testrules.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testrules.cpp; path = tests/testrules.cpp; sourceTree = SOURCE_ROOT; }; + 32DD1B600C014B49ADDB237E /* distributiontable.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = distributiontable.cpp; path = search/distributiontable.cpp; sourceTree = SOURCE_ROOT; }; + 346C96C8324D4BE8A12D1A97 /* testconfig.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testconfig.cpp; path = tests/testconfig.cpp; sourceTree = SOURCE_ROOT; }; + 34B63C891D53453F9C258280 /* threadsafequeue.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = threadsafequeue.cpp; path = core/threadsafequeue.cpp; sourceTree = SOURCE_ROOT; }; + 3D4E9B8ABFBF4DAEB11058E1 /* testboardarea.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testboardarea.cpp; path = tests/testboardarea.cpp; sourceTree = SOURCE_ROOT; }; + 3E097292E4F34AB6806F67E6 /* sgf.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = sgf.cpp; path = dataio/sgf.cpp; sourceTree = SOURCE_ROOT; }; + 3FBACE432776421CAEDF6786 /* play.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = play.cpp; path = program/play.cpp; sourceTree = SOURCE_ROOT; }; + 41CCB0DF860045E5A8697BDD /* testnn.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testnn.cpp; path = tests/testnn.cpp; sourceTree = SOURCE_ROOT; }; + 43CF521030274453B04827E1 /* testsearchv3.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testsearchv3.cpp; path = tests/testsearchv3.cpp; sourceTree = SOURCE_ROOT; }; + 4845ACCEFC204BA89C033482 /* metalbackend.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; indentWidth = 2; name = metalbackend.cpp; path = neuralnet/metalbackend.cpp; sourceTree = SOURCE_ROOT; }; + 48669007B9164F5FB011F549 /* testmisc.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testmisc.cpp; path = tests/testmisc.cpp; sourceTree = SOURCE_ROOT; }; + 4B137CD979C7436188D684A7 /* testnninputs.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testnninputs.cpp; path = tests/testnninputs.cpp; sourceTree = SOURCE_ROOT; }; + 4BF2B81FB1BB43AC81344E4A /* testsearchmisc.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testsearchmisc.cpp; path = tests/testsearchmisc.cpp; sourceTree = SOURCE_ROOT; }; + 4BF5823DCA854224809D93A8 /* commandloop.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = commandloop.cpp; path = core/commandloop.cpp; sourceTree = SOURCE_ROOT; }; + 4F20754875D24724A133A9AE /* numpywrite.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = numpywrite.cpp; path = dataio/numpywrite.cpp; sourceTree = SOURCE_ROOT; }; + 50827347EBFE4467996C3150 /* main.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; indentWidth = 2; path = main.cpp; sourceTree = SOURCE_ROOT; }; + 5185F4BC63B5490AAE4F37CB /* multithread.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = multithread.cpp; path = core/multithread.cpp; sourceTree = SOURCE_ROOT; }; + 540D93E0576C47C789279AF8 /* boardhistory.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = boardhistory.cpp; path = game/boardhistory.cpp; sourceTree = SOURCE_ROOT; }; + 5639F08A96FD467CBD091947 /* test.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = test.cpp; path = core/test.cpp; sourceTree = SOURCE_ROOT; }; + 5902EDD2F6A74BE7966E2001 /* runtests.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = runtests.cpp; path = command/runtests.cpp; sourceTree = SOURCE_ROOT; }; + 59353ECA2B0140FA9365623E /* elo.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = elo.cpp; path = core/elo.cpp; sourceTree = SOURCE_ROOT; }; + 59BC63FBF0804F63A27369AE /* rand_helpers.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = rand_helpers.cpp; path = core/rand_helpers.cpp; sourceTree = SOURCE_ROOT; }; + 5BCE97296A5249A0B49C766F /* gtpconfig.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = gtpconfig.cpp; path = program/gtpconfig.cpp; sourceTree = SOURCE_ROOT; }; + 5D8F26726AAF403C833FBD7F /* desc.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = desc.cpp; path = neuralnet/desc.cpp; sourceTree = SOURCE_ROOT; }; + 63D5831B449B48D1AD132F9F /* makedir.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = makedir.cpp; path = core/makedir.cpp; sourceTree = SOURCE_ROOT; }; + 64D3C3432AB3409C942F7A0E /* misc.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = misc.cpp; path = command/misc.cpp; sourceTree = SOURCE_ROOT; }; + 661A920818694712953495A7 /* testsearchv8.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testsearchv8.cpp; path = tests/testsearchv8.cpp; sourceTree = SOURCE_ROOT; }; + 69300B311DE94520A56A3B5F /* threadtest.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = threadtest.cpp; path = core/threadtest.cpp; sourceTree = SOURCE_ROOT; }; + 6A5C095FD31A4636994B5E5A /* patternbonustable.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = patternbonustable.cpp; path = search/patternbonustable.cpp; sourceTree = SOURCE_ROOT; }; + 6CD97C1775DC4E678823595E /* commandline.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = commandline.cpp; path = command/commandline.cpp; sourceTree = SOURCE_ROOT; }; + 6DA721BDC00F438688E0B241 /* mutexpool.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = mutexpool.cpp; path = search/mutexpool.cpp; sourceTree = SOURCE_ROOT; }; + 6DD28F2EE5FB490F906D63BA /* bookcssjs.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = bookcssjs.cpp; path = book/bookcssjs.cpp; sourceTree = SOURCE_ROOT; }; + 6E87CD61EFA340A1AF4B8BCE /* homedata.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = homedata.cpp; path = dataio/homedata.cpp; sourceTree = SOURCE_ROOT; }; + 6F9788817DEA4417A321C3A0 /* trainingwrite.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = trainingwrite.cpp; path = dataio/trainingwrite.cpp; sourceTree = SOURCE_ROOT; }; + 706365E669744784A6A6DE57 /* reportedsearchvalues.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = reportedsearchvalues.cpp; path = search/reportedsearchvalues.cpp; sourceTree = SOURCE_ROOT; }; + 71DC745C32B543C191262823 /* datetime.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = datetime.cpp; path = core/datetime.cpp; sourceTree = SOURCE_ROOT; }; + 727A790F2FEA4DBEA8ABAE85 /* rules.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = rules.cpp; path = game/rules.cpp; sourceTree = SOURCE_ROOT; }; + 73D2A262E3E542FD8063F8DD /* searchupdatehelpers.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = searchupdatehelpers.cpp; path = search/searchupdatehelpers.cpp; sourceTree = SOURCE_ROOT; }; + 76F8951F199F416F99B96FE8 /* sha2.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = sha2.cpp; path = core/sha2.cpp; sourceTree = SOURCE_ROOT; }; + 77C31BA9C8864C07B491DF1D /* searchtimehelpers.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = searchtimehelpers.cpp; path = search/searchtimehelpers.cpp; sourceTree = SOURCE_ROOT; }; + 7891834D8FB144E0B13F6E21 /* subtreevaluebiastable.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = subtreevaluebiastable.cpp; path = search/subtreevaluebiastable.cpp; sourceTree = SOURCE_ROOT; }; + 792CF6207CA54AABB0F058C6 /* client.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = client.cpp; path = distributed/client.cpp; sourceTree = SOURCE_ROOT; }; + 7A57BA046921422DB33C7614 /* playsettings.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = playsettings.cpp; path = program/playsettings.cpp; sourceTree = SOURCE_ROOT; }; + 7B2C186FF8B3422CB64E6039 /* logger.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = logger.cpp; path = core/logger.cpp; sourceTree = SOURCE_ROOT; }; + 7C7A65C82B4C4AB5B83B1346 /* selfplaymanager.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = selfplaymanager.cpp; path = program/selfplaymanager.cpp; sourceTree = SOURCE_ROOT; }; + 84BCAFD2361F4BE8B5025F65 /* testsymmetries.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testsymmetries.cpp; path = tests/testsymmetries.cpp; sourceTree = SOURCE_ROOT; }; + 888C7B98F8B64150B0903946 /* timecontrols.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = timecontrols.cpp; path = search/timecontrols.cpp; sourceTree = SOURCE_ROOT; }; + 88BAF51D4B34475A90D1D7CC /* testnnevalcanary.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testnnevalcanary.cpp; path = tests/testnnevalcanary.cpp; sourceTree = SOURCE_ROOT; }; + 8C31483CD76D48F2A7327613 /* files.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = files.cpp; path = dataio/files.cpp; sourceTree = SOURCE_ROOT; }; + 8C9D17518AE04398A975E5AE /* testcommon.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testcommon.cpp; path = tests/testcommon.cpp; sourceTree = SOURCE_ROOT; }; + 8F0B49CAFCB24D31808DB2C1 /* board.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = board.cpp; path = game/board.cpp; sourceTree = SOURCE_ROOT; }; + 8FBE5F0F301A405D85F23D38 /* loadmodel.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = loadmodel.cpp; path = dataio/loadmodel.cpp; sourceTree = SOURCE_ROOT; }; + 92C3AF4C79ED491988E9C5BC /* nneval.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = nneval.cpp; path = neuralnet/nneval.cpp; sourceTree = SOURCE_ROOT; }; + 92F4695F66A84118BDCAA13F /* mainargs.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = mainargs.cpp; path = core/mainargs.cpp; sourceTree = SOURCE_ROOT; }; + 93FF01FEC8DA40DB916C4F0A /* search.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = search.cpp; path = search/search.cpp; sourceTree = SOURCE_ROOT; }; + 948AF9E88374487D85E846C2 /* match.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = match.cpp; path = command/match.cpp; sourceTree = SOURCE_ROOT; }; + 952F0B54C8BF410C9EA67989 /* testsgf.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testsgf.cpp; path = tests/testsgf.cpp; sourceTree = SOURCE_ROOT; }; + 973B04213D1B4030B35FB01C /* book.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = book.cpp; path = book/book.cpp; sourceTree = SOURCE_ROOT; }; + 9FB3A34B1C8D4CBF9997DDA7 /* playutils.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = playutils.cpp; path = program/playutils.cpp; sourceTree = SOURCE_ROOT; }; + A241D7415C384D3A81BF73AC /* tune.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = tune.cpp; path = command/tune.cpp; sourceTree = SOURCE_ROOT; }; + A255C9FAA2E145048F33368C /* testtime.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testtime.cpp; path = tests/testtime.cpp; sourceTree = SOURCE_ROOT; }; + A72EC47D68904D38A5EAE635 /* searchhelpers.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = searchhelpers.cpp; path = search/searchhelpers.cpp; sourceTree = SOURCE_ROOT; }; + A8748F2EFAAF401DACE6B60A /* global.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = global.cpp; path = core/global.cpp; sourceTree = SOURCE_ROOT; }; + AA6C3E7D4604497D8B94AC50 /* searchnnhelpers.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = searchnnhelpers.cpp; path = search/searchnnhelpers.cpp; sourceTree = SOURCE_ROOT; }; + AD94201E380643C3985E9D62 /* gtp.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = gtp.cpp; path = command/gtp.cpp; sourceTree = SOURCE_ROOT; }; + AFF33AEBABB1472B9F241A98 /* selfplay.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = selfplay.cpp; path = command/selfplay.cpp; sourceTree = SOURCE_ROOT; }; + B2460699580B49F689D028D5 /* genbook.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = genbook.cpp; path = command/genbook.cpp; sourceTree = SOURCE_ROOT; }; + B8E283A3B8004F289DACCD8A /* rand.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = rand.cpp; path = core/rand.cpp; sourceTree = SOURCE_ROOT; }; + BC9F65190B644C969D327CD9 /* testsearchnonn.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testsearchnonn.cpp; path = tests/testsearchnonn.cpp; sourceTree = SOURCE_ROOT; }; + BCBCE4A8D83F42FBA4EA0CBE /* searchmultithreadhelpers.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = searchmultithreadhelpers.cpp; path = search/searchmultithreadhelpers.cpp; sourceTree = SOURCE_ROOT; }; + BDF52FD481AA424BBC59124D /* hash.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = hash.cpp; path = core/hash.cpp; sourceTree = SOURCE_ROOT; }; + BE70F73F685D4EDA9977822F /* tinymodel.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = tinymodel.cpp; path = tests/tinymodel.cpp; sourceTree = SOURCE_ROOT; }; + BE7F7520CA15440EBDF0A21D /* md5.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = md5.cpp; path = core/md5.cpp; sourceTree = SOURCE_ROOT; }; + BF423768A6B74FF18FDC44E7 /* analysisdata.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = analysisdata.cpp; path = search/analysisdata.cpp; sourceTree = SOURCE_ROOT; }; + C33571C53ECC4C82B0A9DA7D /* searchnodetable.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = searchnodetable.cpp; path = search/searchnodetable.cpp; sourceTree = SOURCE_ROOT; }; + CA66CE9038574A0BB16D80B6 /* evalsgf.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = evalsgf.cpp; path = command/evalsgf.cpp; sourceTree = SOURCE_ROOT; }; + CAD1B260FFB74AF9BA66A58A /* fileutils.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = fileutils.cpp; path = core/fileutils.cpp; sourceTree = SOURCE_ROOT; }; + D104762E63AF4C6A8ADB220E /* setup.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; indentWidth = 2; name = setup.cpp; path = program/setup.cpp; sourceTree = SOURCE_ROOT; }; + D1DFBE2386CE449D82894520 /* testtrainingwrite.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testtrainingwrite.cpp; path = tests/testtrainingwrite.cpp; sourceTree = SOURCE_ROOT; }; + D41000BDB70543A4820D445A /* nninputs.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = nninputs.cpp; path = neuralnet/nninputs.cpp; sourceTree = SOURCE_ROOT; }; + D49AE95F1DD947B5BFF58C1F /* contribute.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = contribute.cpp; path = command/contribute.cpp; sourceTree = SOURCE_ROOT; }; + D61629242F5143EBB2D9BEC9 /* base64.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = base64.cpp; path = core/base64.cpp; sourceTree = SOURCE_ROOT; }; + D645BB8AAF424700A75ED223 /* threadsafecounter.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = threadsafecounter.cpp; path = core/threadsafecounter.cpp; sourceTree = SOURCE_ROOT; }; + D8710CF2CCA3478EB65063C6 /* gatekeeper.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = gatekeeper.cpp; path = command/gatekeeper.cpp; sourceTree = SOURCE_ROOT; }; + DD4302F4D69E4EE98EA75B2C /* localpattern.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = localpattern.cpp; path = search/localpattern.cpp; sourceTree = SOURCE_ROOT; }; + DDCAE99038794BE8B4BB3962 /* modelversion.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = modelversion.cpp; path = neuralnet/modelversion.cpp; sourceTree = SOURCE_ROOT; }; + E10ACAF52928A6D30004AB17 /* katago */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = katago; sourceTree = BUILT_PRODUCTS_DIR; }; + E10ACAF92928A8160004AB17 /* coremlbackend.h */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = coremlbackend.h; path = neuralnet/coremlbackend.h; sourceTree = ""; tabWidth = 4; }; + E12453D42A1CF0DE0062DF9C /* testbook.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testbook.cpp; path = tests/testbook.cpp; sourceTree = ""; }; + E12453D62A1D015E0062DF9C /* poswriter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = poswriter.cpp; path = dataio/poswriter.cpp; sourceTree = ""; }; + E12EC2172B10D61E0024E274 /* coremlbackend.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = coremlbackend.swift; path = neuralnet/coremlbackend.swift; sourceTree = ""; }; + E12EC2182B10D61E0024E274 /* metalbackend.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = metalbackend.swift; path = neuralnet/metalbackend.swift; sourceTree = ""; }; + E12EC2192B10D61E0024E274 /* coremlmodel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = coremlmodel.swift; path = neuralnet/coremlmodel.swift; sourceTree = ""; }; + E12EC2242B10E0520024E274 /* coreml_analysis.cfg */ = {isa = PBXFileReference; lastKnownFileType = text; name = coreml_analysis.cfg; path = configs/misc/coreml_analysis.cfg; sourceTree = ""; }; + E12EC2252B10E0520024E274 /* coreml_example.cfg */ = {isa = PBXFileReference; lastKnownFileType = text; name = coreml_example.cfg; path = configs/misc/coreml_example.cfg; sourceTree = ""; }; + E12EC22F2B1237440024E274 /* parallel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = parallel.cpp; path = core/parallel.cpp; sourceTree = ""; }; + E12EC2322B12375B0024E274 /* writetrainingdata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = writetrainingdata.cpp; path = command/writetrainingdata.cpp; sourceTree = ""; }; + E13CF66228E1896C005CB016 /* coremlbackend.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; name = coremlbackend.cpp; path = neuralnet/coremlbackend.cpp; sourceTree = ""; }; + E157FDCC2AF7CE2300E25677 /* katagotest.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = katagotest.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + E157FDCE2AF7CE2500E25677 /* testnn.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = testnn.mm; sourceTree = ""; }; + E1605CE12BFAD6EB00A4B872 /* sgfmetadata.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; name = sgfmetadata.cpp; path = neuralnet/sgfmetadata.cpp; sourceTree = SOURCE_ROOT; }; + E16BC82C2C4A8AEB00EA3A1E /* ModelTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelTest.swift; sourceTree = ""; }; + E16BC82E2C4B461500EA3A1E /* CoreMLBackendTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreMLBackendTest.swift; sourceTree = ""; }; + E16BC8342C4B835F00EA3A1E /* CoreMLModelTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreMLModelTest.swift; sourceTree = ""; }; + E17D098A294D45CF005968E9 /* gputest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = gputest.cpp; path = command/gputest.cpp; sourceTree = ""; }; + E184464D2BFFF6A1004F5E3B /* misc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = misc.swift; path = neuralnet/misc.swift; sourceTree = ""; }; + E199A6F828E25E8100A2E051 /* metalbridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = metalbridge.h; path = neuralnet/metalbridge.h; sourceTree = ""; }; + E199A6F928E25EE500A2E051 /* metalbackend.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = metalbackend.h; path = neuralnet/metalbackend.h; sourceTree = ""; }; + E1AD404928E1D59700E41968 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; }; + E1AD404A28E1D59700E41968 /* MetalPerformanceShaders.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalPerformanceShaders.framework; path = System/Library/Frameworks/MetalPerformanceShaders.framework; sourceTree = SDKROOT; }; + E1AD404B28E1D59700E41968 /* MetalPerformanceShadersGraph.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalPerformanceShadersGraph.framework; path = System/Library/Frameworks/MetalPerformanceShadersGraph.framework; sourceTree = SDKROOT; }; + E1AD404F28E1D5A700E41968 /* CoreML.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreML.framework; path = System/Library/Frameworks/CoreML.framework; sourceTree = SDKROOT; }; + E1AD405128E1D75B00E41968 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + E1DACF4C2B08997300082FF7 /* KataGoSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KataGoSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E1DACF622B089B5500082FF7 /* KataGoSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KataGoSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + E1DACF642B089B5500082FF7 /* KataGoSwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KataGoSwiftTests.swift; sourceTree = ""; }; + E3F8D82F94E14F11BA0F59E6 /* testscore.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testscore.cpp; path = tests/testscore.cpp; sourceTree = SOURCE_ROOT; }; + E7B41A9FE4124FA1AB3FBEF1 /* analysis.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = analysis.cpp; path = command/analysis.cpp; sourceTree = SOURCE_ROOT; }; + EC59266A435045C5B84F9105 /* searchexplorehelpers.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = searchexplorehelpers.cpp; path = search/searchexplorehelpers.cpp; sourceTree = SOURCE_ROOT; }; + EEB543E9A42948748BF883C3 /* timer.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = timer.cpp; path = core/timer.cpp; sourceTree = SOURCE_ROOT; }; + F18310A722494DAEACBE09BC /* testboardbasic.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = testboardbasic.cpp; path = tests/testboardbasic.cpp; sourceTree = SOURCE_ROOT; }; + F2D4BF5BF0CD446F80DFDACE /* asyncbot.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = asyncbot.cpp; path = search/asyncbot.cpp; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E10ACAEB2928A6D30004AB17 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E1DACF5D2B089A5400082FF7 /* KataGoSwift.framework in Frameworks */, + E10ACAEC2928A6D30004AB17 /* MetalPerformanceShaders.framework in Frameworks */, + E10ACAED2928A6D30004AB17 /* libz.tbd in Frameworks */, + E10ACAFD2928BBF00004AB17 /* CoreML.framework in Frameworks */, + E10ACAEE2928A6D30004AB17 /* Metal.framework in Frameworks */, + E10ACAEF2928A6D30004AB17 /* MetalPerformanceShadersGraph.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E157FDC92AF7CE2300E25677 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E1DACF732B089C7700082FF7 /* KataGoSwift.framework in Frameworks */, + E157FE4A2AF7D22800E25677 /* MetalPerformanceShaders.framework in Frameworks */, + E157FE4B2AF7D23800E25677 /* libz.tbd in Frameworks */, + E157FE4C2AF7D2E400E25677 /* CoreML.framework in Frameworks */, + E157FE4D2AF7D2E800E25677 /* Metal.framework in Frameworks */, + E157FE4E2AF7D2ED00E25677 /* MetalPerformanceShadersGraph.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E1DACF492B08997300082FF7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E1DACF5F2B089B5500082FF7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 29C8B1F369034337B2CC96EF = { + isa = PBXGroup; + children = ( + E1AD404828E1D59700E41968 /* Frameworks */, + 30DEE4A41280490EA8216883 /* KataGo */, + E1DACF632B089B5500082FF7 /* KataGoSwiftTests */, + E1E29E1128F5B05300E73FF8 /* KataGoTest */, + 8218F7988402482BAFDA7E88 /* Products */, + E12EC2232B10E01E0024E274 /* Resources */, + ); + sourceTree = ""; + }; + 30DEE4A41280490EA8216883 /* KataGo */ = { + isa = PBXGroup; + children = ( + E42DAD7F6DF94192AED73FF1 /* Source Files */, + 3B22C5B3776049BD9CC4D5D9 /* Header Files */, + ); + name = KataGo; + sourceTree = ""; + }; + 3B22C5B3776049BD9CC4D5D9 /* Header Files */ = { + isa = PBXGroup; + children = ( + E10ACAF92928A8160004AB17 /* coremlbackend.h */, + E199A6F928E25EE500A2E051 /* metalbackend.h */, + E199A6F828E25E8100A2E051 /* metalbridge.h */, + ); + name = "Header Files"; + sourceTree = ""; + }; + 8218F7988402482BAFDA7E88 /* Products */ = { + isa = PBXGroup; + children = ( + E10ACAF52928A6D30004AB17 /* katago */, + E157FDCC2AF7CE2300E25677 /* katagotest.xctest */, + E1DACF4C2B08997300082FF7 /* KataGoSwift.framework */, + E1DACF622B089B5500082FF7 /* KataGoSwiftTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + E12EC2232B10E01E0024E274 /* Resources */ = { + isa = PBXGroup; + children = ( + E12EC2242B10E0520024E274 /* coreml_analysis.cfg */, + E12EC2252B10E0520024E274 /* coreml_example.cfg */, + ); + name = Resources; + sourceTree = ""; + }; + E1AD404828E1D59700E41968 /* Frameworks */ = { + isa = PBXGroup; + children = ( + E1AD405128E1D75B00E41968 /* libz.tbd */, + E1AD404F28E1D5A700E41968 /* CoreML.framework */, + E1AD404928E1D59700E41968 /* Metal.framework */, + E1AD404A28E1D59700E41968 /* MetalPerformanceShaders.framework */, + E1AD404B28E1D59700E41968 /* MetalPerformanceShadersGraph.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + E1DACF632B089B5500082FF7 /* KataGoSwiftTests */ = { + isa = PBXGroup; + children = ( + E1DACF642B089B5500082FF7 /* KataGoSwiftTests.swift */, + E16BC82C2C4A8AEB00EA3A1E /* ModelTest.swift */, + E16BC82E2C4B461500EA3A1E /* CoreMLBackendTest.swift */, + E16BC8342C4B835F00EA3A1E /* CoreMLModelTest.swift */, + ); + name = KataGoSwiftTests; + path = xcode/KataGoSwiftTests; + sourceTree = ""; + }; + E1E29E1128F5B05300E73FF8 /* KataGoTest */ = { + isa = PBXGroup; + children = ( + E157FDCE2AF7CE2500E25677 /* testnn.mm */, + ); + name = KataGoTest; + path = xcode/KataGoTest; + sourceTree = ""; + }; + E42DAD7F6DF94192AED73FF1 /* Source Files */ = { + isa = PBXGroup; + children = ( + E7B41A9FE4124FA1AB3FBEF1 /* analysis.cpp */, + BF423768A6B74FF18FDC44E7 /* analysisdata.cpp */, + F2D4BF5BF0CD446F80DFDACE /* asyncbot.cpp */, + D61629242F5143EBB2D9BEC9 /* base64.cpp */, + 063E4C878E7E43858A863A78 /* benchmark.cpp */, + 8F0B49CAFCB24D31808DB2C1 /* board.cpp */, + 540D93E0576C47C789279AF8 /* boardhistory.cpp */, + 973B04213D1B4030B35FB01C /* book.cpp */, + 6DD28F2EE5FB490F906D63BA /* bookcssjs.cpp */, + 176C18FD215D45179B93393C /* bsearch.cpp */, + 792CF6207CA54AABB0F058C6 /* client.cpp */, + 6CD97C1775DC4E678823595E /* commandline.cpp */, + 4BF5823DCA854224809D93A8 /* commandloop.cpp */, + 23D034621365403182419780 /* config_parser.cpp */, + D49AE95F1DD947B5BFF58C1F /* contribute.cpp */, + E13CF66228E1896C005CB016 /* coremlbackend.cpp */, + E12EC2172B10D61E0024E274 /* coremlbackend.swift */, + E12EC2192B10D61E0024E274 /* coremlmodel.swift */, + 71DC745C32B543C191262823 /* datetime.cpp */, + 5D8F26726AAF403C833FBD7F /* desc.cpp */, + 32DD1B600C014B49ADDB237E /* distributiontable.cpp */, + 59353ECA2B0140FA9365623E /* elo.cpp */, + CA66CE9038574A0BB16D80B6 /* evalsgf.cpp */, + 2626105D31ED44D98E6B9B9D /* fancymath.cpp */, + 8C31483CD76D48F2A7327613 /* files.cpp */, + CAD1B260FFB74AF9BA66A58A /* fileutils.cpp */, + D8710CF2CCA3478EB65063C6 /* gatekeeper.cpp */, + B2460699580B49F689D028D5 /* genbook.cpp */, + A8748F2EFAAF401DACE6B60A /* global.cpp */, + E17D098A294D45CF005968E9 /* gputest.cpp */, + 10EB7D2538F94B26BE1B1740 /* graphhash.cpp */, + AD94201E380643C3985E9D62 /* gtp.cpp */, + 5BCE97296A5249A0B49C766F /* gtpconfig.cpp */, + BDF52FD481AA424BBC59124D /* hash.cpp */, + 6E87CD61EFA340A1AF4B8BCE /* homedata.cpp */, + 8FBE5F0F301A405D85F23D38 /* loadmodel.cpp */, + DD4302F4D69E4EE98EA75B2C /* localpattern.cpp */, + 7B2C186FF8B3422CB64E6039 /* logger.cpp */, + 50827347EBFE4467996C3150 /* main.cpp */, + 92F4695F66A84118BDCAA13F /* mainargs.cpp */, + 63D5831B449B48D1AD132F9F /* makedir.cpp */, + 948AF9E88374487D85E846C2 /* match.cpp */, + BE7F7520CA15440EBDF0A21D /* md5.cpp */, + 4845ACCEFC204BA89C033482 /* metalbackend.cpp */, + E12EC2182B10D61E0024E274 /* metalbackend.swift */, + 64D3C3432AB3409C942F7A0E /* misc.cpp */, + E184464D2BFFF6A1004F5E3B /* misc.swift */, + DDCAE99038794BE8B4BB3962 /* modelversion.cpp */, + 5185F4BC63B5490AAE4F37CB /* multithread.cpp */, + 6DA721BDC00F438688E0B241 /* mutexpool.cpp */, + 92C3AF4C79ED491988E9C5BC /* nneval.cpp */, + D41000BDB70543A4820D445A /* nninputs.cpp */, + 4F20754875D24724A133A9AE /* numpywrite.cpp */, + E12EC22F2B1237440024E274 /* parallel.cpp */, + 6A5C095FD31A4636994B5E5A /* patternbonustable.cpp */, + 3FBACE432776421CAEDF6786 /* play.cpp */, + 7A57BA046921422DB33C7614 /* playsettings.cpp */, + 9FB3A34B1C8D4CBF9997DDA7 /* playutils.cpp */, + E12453D62A1D015E0062DF9C /* poswriter.cpp */, + 59BC63FBF0804F63A27369AE /* rand_helpers.cpp */, + B8E283A3B8004F289DACCD8A /* rand.cpp */, + 706365E669744784A6A6DE57 /* reportedsearchvalues.cpp */, + 727A790F2FEA4DBEA8ABAE85 /* rules.cpp */, + 5902EDD2F6A74BE7966E2001 /* runtests.cpp */, + 11318DB744F340DCB41F7248 /* sandbox.cpp */, + 93FF01FEC8DA40DB916C4F0A /* search.cpp */, + EC59266A435045C5B84F9105 /* searchexplorehelpers.cpp */, + A72EC47D68904D38A5EAE635 /* searchhelpers.cpp */, + 07DAAE05A9FA46F5B271903E /* searchmirror.cpp */, + BCBCE4A8D83F42FBA4EA0CBE /* searchmultithreadhelpers.cpp */, + AA6C3E7D4604497D8B94AC50 /* searchnnhelpers.cpp */, + 206727F6853C468F84FC44AE /* searchnode.cpp */, + C33571C53ECC4C82B0A9DA7D /* searchnodetable.cpp */, + 1660F43339464F1F82D603C2 /* searchparams.cpp */, + 1BAD528CE45E4D31A6F0F058 /* searchresults.cpp */, + 77C31BA9C8864C07B491DF1D /* searchtimehelpers.cpp */, + 73D2A262E3E542FD8063F8DD /* searchupdatehelpers.cpp */, + AFF33AEBABB1472B9F241A98 /* selfplay.cpp */, + 7C7A65C82B4C4AB5B83B1346 /* selfplaymanager.cpp */, + D104762E63AF4C6A8ADB220E /* setup.cpp */, + 3E097292E4F34AB6806F67E6 /* sgf.cpp */, + E1605CE12BFAD6EB00A4B872 /* sgfmetadata.cpp */, + 76F8951F199F416F99B96FE8 /* sha2.cpp */, + 7891834D8FB144E0B13F6E21 /* subtreevaluebiastable.cpp */, + 5639F08A96FD467CBD091947 /* test.cpp */, + 3D4E9B8ABFBF4DAEB11058E1 /* testboardarea.cpp */, + F18310A722494DAEACBE09BC /* testboardbasic.cpp */, + E12453D42A1CF0DE0062DF9C /* testbook.cpp */, + 8C9D17518AE04398A975E5AE /* testcommon.cpp */, + 346C96C8324D4BE8A12D1A97 /* testconfig.cpp */, + 48669007B9164F5FB011F549 /* testmisc.cpp */, + 41CCB0DF860045E5A8697BDD /* testnn.cpp */, + 88BAF51D4B34475A90D1D7CC /* testnnevalcanary.cpp */, + 4B137CD979C7436188D684A7 /* testnninputs.cpp */, + 0F8F91005809465EB2EDD409 /* testownership.cpp */, + 2F5B917DA90147ABBAC18571 /* testrules.cpp */, + E3F8D82F94E14F11BA0F59E6 /* testscore.cpp */, + 0E2F9938E72849F691272AA0 /* testsearch.cpp */, + 0EDC97A2834E434691EA91C1 /* testsearchcommon.cpp */, + 4BF2B81FB1BB43AC81344E4A /* testsearchmisc.cpp */, + BC9F65190B644C969D327CD9 /* testsearchnonn.cpp */, + 43CF521030274453B04827E1 /* testsearchv3.cpp */, + 661A920818694712953495A7 /* testsearchv8.cpp */, + 1356448A03004176848C790A /* testsearchv9.cpp */, + 952F0B54C8BF410C9EA67989 /* testsgf.cpp */, + 84BCAFD2361F4BE8B5025F65 /* testsymmetries.cpp */, + A255C9FAA2E145048F33368C /* testtime.cpp */, + D1DFBE2386CE449D82894520 /* testtrainingwrite.cpp */, + D645BB8AAF424700A75ED223 /* threadsafecounter.cpp */, + 34B63C891D53453F9C258280 /* threadsafequeue.cpp */, + 69300B311DE94520A56A3B5F /* threadtest.cpp */, + 888C7B98F8B64150B0903946 /* timecontrols.cpp */, + EEB543E9A42948748BF883C3 /* timer.cpp */, + BE70F73F685D4EDA9977822F /* tinymodel.cpp */, + 279C4ABB40FE447483F0F975 /* tinymodeldata.cpp */, + 6F9788817DEA4417A321C3A0 /* trainingwrite.cpp */, + A241D7415C384D3A81BF73AC /* tune.cpp */, + E12EC2322B12375B0024E274 /* writetrainingdata.cpp */, + ); + name = "Source Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + E1DACF472B08997300082FF7 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + E10ACA7B2928A6D30004AB17 /* katago */ = { + isa = PBXNativeTarget; + buildConfigurationList = E10ACAF02928A6D30004AB17 /* Build configuration list for PBXNativeTarget "katago" */; + buildPhases = ( + E10ACA7C2928A6D30004AB17 /* Sources */, + E10ACAEB2928A6D30004AB17 /* Frameworks */, + E12EC22D2B10E3200024E274 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + E1DACF5C2B089A4B00082FF7 /* PBXTargetDependency */, + ); + name = katago; + productName = katago; + productReference = E10ACAF52928A6D30004AB17 /* katago */; + productType = "com.apple.product-type.tool"; + }; + E157FDCB2AF7CE2300E25677 /* katagotest */ = { + isa = PBXNativeTarget; + buildConfigurationList = E157FDD42AF7CE2500E25677 /* Build configuration list for PBXNativeTarget "katagotest" */; + buildPhases = ( + E157FDC82AF7CE2300E25677 /* Sources */, + E157FDC92AF7CE2300E25677 /* Frameworks */, + E157FDCA2AF7CE2300E25677 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + E1DACF722B089C6F00082FF7 /* PBXTargetDependency */, + ); + name = katagotest; + productName = testc; + productReference = E157FDCC2AF7CE2300E25677 /* katagotest.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + E1DACF4B2B08997300082FF7 /* KataGoSwift */ = { + isa = PBXNativeTarget; + buildConfigurationList = E1DACF542B08997400082FF7 /* Build configuration list for PBXNativeTarget "KataGoSwift" */; + buildPhases = ( + E1DACF472B08997300082FF7 /* Headers */, + E1DACF482B08997300082FF7 /* Sources */, + E1DACF492B08997300082FF7 /* Frameworks */, + E1DACF4A2B08997300082FF7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = KataGoSwift; + productName = KataGoSwift; + productReference = E1DACF4C2B08997300082FF7 /* KataGoSwift.framework */; + productType = "com.apple.product-type.framework"; + }; + E1DACF612B089B5500082FF7 /* KataGoSwiftTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = E1DACF692B089B5500082FF7 /* Build configuration list for PBXNativeTarget "KataGoSwiftTests" */; + buildPhases = ( + E1DACF5E2B089B5500082FF7 /* Sources */, + E1DACF5F2B089B5500082FF7 /* Frameworks */, + E1DACF602B089B5500082FF7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = KataGoSwiftTests; + productName = KataGoSwiftTests; + productReference = E1DACF622B089B5500082FF7 /* KataGoSwiftTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 91644CF2108748368B902DCE /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + DefaultBuildSystemTypeForWorkspace = Latest; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; + TargetAttributes = { + E157FDCB2AF7CE2300E25677 = { + CreatedOnToolsVersion = 15.0.1; + LastSwiftMigration = 1540; + }; + E1DACF4B2B08997300082FF7 = { + CreatedOnToolsVersion = 15.0.1; + LastSwiftMigration = 1500; + }; + E1DACF612B089B5500082FF7 = { + CreatedOnToolsVersion = 15.0.1; + }; + }; + }; + buildConfigurationList = 0838DC7C409844AFA516AAE2 /* Build configuration list for PBXProject "KataGo" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 29C8B1F369034337B2CC96EF; + projectDirPath = ../; + projectRoot = ""; + targets = ( + E10ACA7B2928A6D30004AB17 /* katago */, + E157FDCB2AF7CE2300E25677 /* katagotest */, + E1DACF4B2B08997300082FF7 /* KataGoSwift */, + E1DACF612B089B5500082FF7 /* KataGoSwiftTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E157FDCA2AF7CE2300E25677 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E1DACF4A2B08997300082FF7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E1DACF602B089B5500082FF7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E10ACA7C2928A6D30004AB17 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E1605CE22BFAD6EB00A4B872 /* sgfmetadata.cpp in Sources */, + E10ACA7D2928A6D30004AB17 /* book.cpp in Sources */, + E10ACA7E2928A6D30004AB17 /* bookcssjs.cpp in Sources */, + E10ACA7F2928A6D30004AB17 /* analysis.cpp in Sources */, + E10ACA802928A6D30004AB17 /* benchmark.cpp in Sources */, + E17D098C294D45CF005968E9 /* gputest.cpp in Sources */, + E10ACA812928A6D30004AB17 /* commandline.cpp in Sources */, + E10ACA822928A6D30004AB17 /* contribute.cpp in Sources */, + E10ACA832928A6D30004AB17 /* evalsgf.cpp in Sources */, + E10ACA842928A6D30004AB17 /* gatekeeper.cpp in Sources */, + E10ACA862928A6D30004AB17 /* genbook.cpp in Sources */, + E12453D72A1D015E0062DF9C /* poswriter.cpp in Sources */, + E10ACA872928A6D30004AB17 /* gtp.cpp in Sources */, + E10ACA882928A6D30004AB17 /* match.cpp in Sources */, + E10ACA8A2928A6D30004AB17 /* misc.cpp in Sources */, + E10ACA8B2928A6D30004AB17 /* runtests.cpp in Sources */, + E10ACA8C2928A6D30004AB17 /* sandbox.cpp in Sources */, + E10ACA8D2928A6D30004AB17 /* selfplay.cpp in Sources */, + E10ACA8E2928A6D30004AB17 /* tune.cpp in Sources */, + E10ACA8F2928A6D30004AB17 /* base64.cpp in Sources */, + E10ACA902928A6D30004AB17 /* bsearch.cpp in Sources */, + E12EC2332B12375C0024E274 /* writetrainingdata.cpp in Sources */, + E10ACA912928A6D30004AB17 /* commandloop.cpp in Sources */, + E10ACA922928A6D30004AB17 /* config_parser.cpp in Sources */, + E10ACA932928A6D30004AB17 /* datetime.cpp in Sources */, + E10ACA942928A6D30004AB17 /* elo.cpp in Sources */, + E10ACA952928A6D30004AB17 /* fancymath.cpp in Sources */, + E10ACA962928A6D30004AB17 /* fileutils.cpp in Sources */, + E12EC2302B1237440024E274 /* parallel.cpp in Sources */, + E10ACA972928A6D30004AB17 /* global.cpp in Sources */, + E10ACA982928A6D30004AB17 /* hash.cpp in Sources */, + E10ACA992928A6D30004AB17 /* logger.cpp in Sources */, + E10ACA9A2928A6D30004AB17 /* mainargs.cpp in Sources */, + E10ACA9B2928A6D30004AB17 /* makedir.cpp in Sources */, + E10ACA9C2928A6D30004AB17 /* md5.cpp in Sources */, + E10ACA9D2928A6D30004AB17 /* multithread.cpp in Sources */, + E10ACA9E2928A6D30004AB17 /* rand.cpp in Sources */, + E10ACA9F2928A6D30004AB17 /* rand_helpers.cpp in Sources */, + E12453D52A1CF0DE0062DF9C /* testbook.cpp in Sources */, + E10ACAA02928A6D30004AB17 /* sha2.cpp in Sources */, + E10ACAA12928A6D30004AB17 /* test.cpp in Sources */, + E10ACAA22928A6D30004AB17 /* threadsafecounter.cpp in Sources */, + E10ACAA32928A6D30004AB17 /* threadsafequeue.cpp in Sources */, + E10ACAA42928A6D30004AB17 /* threadtest.cpp in Sources */, + E10ACAA52928A6D30004AB17 /* timer.cpp in Sources */, + E10ACAA62928A6D30004AB17 /* files.cpp in Sources */, + E10ACAA72928A6D30004AB17 /* homedata.cpp in Sources */, + E10ACAA82928A6D30004AB17 /* loadmodel.cpp in Sources */, + E10ACAA92928A6D30004AB17 /* numpywrite.cpp in Sources */, + E10ACAAA2928A6D30004AB17 /* sgf.cpp in Sources */, + E10ACAAB2928A6D30004AB17 /* trainingwrite.cpp in Sources */, + E10ACAAC2928A6D30004AB17 /* client.cpp in Sources */, + E10ACAAD2928A6D30004AB17 /* board.cpp in Sources */, + E10ACAAE2928A6D30004AB17 /* boardhistory.cpp in Sources */, + E10ACAAF2928A6D30004AB17 /* graphhash.cpp in Sources */, + E10ACAB02928A6D30004AB17 /* rules.cpp in Sources */, + E10ACAB12928A6D30004AB17 /* main.cpp in Sources */, + E10ACAB22928A6D30004AB17 /* desc.cpp in Sources */, + E10ACAB32928A6D30004AB17 /* metalbackend.cpp in Sources */, + E10ACAB52928A6D30004AB17 /* modelversion.cpp in Sources */, + E10ACAB62928A6D30004AB17 /* nneval.cpp in Sources */, + E10ACAB72928A6D30004AB17 /* nninputs.cpp in Sources */, + E10ACAB82928A6D30004AB17 /* gtpconfig.cpp in Sources */, + E10ACAB92928A6D30004AB17 /* play.cpp in Sources */, + E10ACABA2928A6D30004AB17 /* playsettings.cpp in Sources */, + E10ACABB2928A6D30004AB17 /* playutils.cpp in Sources */, + E10ACABC2928A6D30004AB17 /* selfplaymanager.cpp in Sources */, + E10ACABD2928A6D30004AB17 /* setup.cpp in Sources */, + E10ACABE2928A6D30004AB17 /* analysisdata.cpp in Sources */, + E10ACABF2928A6D30004AB17 /* asyncbot.cpp in Sources */, + E10ACAC02928A6D30004AB17 /* distributiontable.cpp in Sources */, + E10ACAC12928A6D30004AB17 /* localpattern.cpp in Sources */, + E10ACAC22928A6D30004AB17 /* mutexpool.cpp in Sources */, + E10ACAC32928A6D30004AB17 /* patternbonustable.cpp in Sources */, + E10ACAC42928A6D30004AB17 /* reportedsearchvalues.cpp in Sources */, + E10ACAC52928A6D30004AB17 /* search.cpp in Sources */, + E10ACAC62928A6D30004AB17 /* searchexplorehelpers.cpp in Sources */, + E10ACAC72928A6D30004AB17 /* searchhelpers.cpp in Sources */, + E10ACAC82928A6D30004AB17 /* searchmirror.cpp in Sources */, + E10ACAC92928A6D30004AB17 /* searchmultithreadhelpers.cpp in Sources */, + E10ACACA2928A6D30004AB17 /* searchnnhelpers.cpp in Sources */, + E10ACACB2928A6D30004AB17 /* searchnode.cpp in Sources */, + E10ACACC2928A6D30004AB17 /* searchnodetable.cpp in Sources */, + E10ACACD2928A6D30004AB17 /* searchparams.cpp in Sources */, + E10ACACE2928A6D30004AB17 /* searchresults.cpp in Sources */, + E10ACACF2928A6D30004AB17 /* searchtimehelpers.cpp in Sources */, + E10ACAD02928A6D30004AB17 /* searchupdatehelpers.cpp in Sources */, + E10ACAD12928A6D30004AB17 /* subtreevaluebiastable.cpp in Sources */, + E10ACAD22928A6D30004AB17 /* timecontrols.cpp in Sources */, + E10ACAD32928A6D30004AB17 /* testboardarea.cpp in Sources */, + E10ACAD42928A6D30004AB17 /* testboardbasic.cpp in Sources */, + E10ACAD52928A6D30004AB17 /* testcommon.cpp in Sources */, + E10ACAD62928A6D30004AB17 /* testconfig.cpp in Sources */, + E10ACAD72928A6D30004AB17 /* testmisc.cpp in Sources */, + E10ACAD82928A6D30004AB17 /* testnn.cpp in Sources */, + E10ACAD92928A6D30004AB17 /* testnnevalcanary.cpp in Sources */, + E10ACADA2928A6D30004AB17 /* testnninputs.cpp in Sources */, + E10ACADB2928A6D30004AB17 /* testownership.cpp in Sources */, + E10ACADC2928A6D30004AB17 /* testrules.cpp in Sources */, + E10ACADD2928A6D30004AB17 /* testscore.cpp in Sources */, + E10ACADE2928A6D30004AB17 /* testsearch.cpp in Sources */, + E10ACADF2928A6D30004AB17 /* testsearchcommon.cpp in Sources */, + E10ACAE02928A6D30004AB17 /* testsearchmisc.cpp in Sources */, + E10ACAE12928A6D30004AB17 /* testsearchnonn.cpp in Sources */, + E10ACAE22928A6D30004AB17 /* testsearchv3.cpp in Sources */, + E10ACAE32928A6D30004AB17 /* testsearchv8.cpp in Sources */, + E10ACAE42928A6D30004AB17 /* testsearchv9.cpp in Sources */, + E10ACAE52928A6D30004AB17 /* testsgf.cpp in Sources */, + E10ACAE62928A6D30004AB17 /* testsymmetries.cpp in Sources */, + E10ACAFA2928A8D30004AB17 /* coremlbackend.cpp in Sources */, + E10ACAE72928A6D30004AB17 /* testtime.cpp in Sources */, + E10ACAE82928A6D30004AB17 /* testtrainingwrite.cpp in Sources */, + E10ACAE92928A6D30004AB17 /* tinymodel.cpp in Sources */, + E10ACAEA2928A6D30004AB17 /* tinymodeldata.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E157FDC82AF7CE2300E25677 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E157FE4F2AF7DA1600E25677 /* testnn.mm in Sources */, + E157FDD82AF7D1E500E25677 /* analysis.cpp in Sources */, + E157FDD92AF7D1E500E25677 /* analysisdata.cpp in Sources */, + E157FDDA2AF7D1E500E25677 /* asyncbot.cpp in Sources */, + E157FDDB2AF7D1E500E25677 /* base64.cpp in Sources */, + E157FDDC2AF7D1E500E25677 /* benchmark.cpp in Sources */, + E157FDDD2AF7D1E500E25677 /* board.cpp in Sources */, + E157FDDE2AF7D1E500E25677 /* boardhistory.cpp in Sources */, + E157FDDF2AF7D1E500E25677 /* book.cpp in Sources */, + E157FDE02AF7D1E500E25677 /* bookcssjs.cpp in Sources */, + E157FDE12AF7D1E500E25677 /* bsearch.cpp in Sources */, + E157FDE22AF7D1E500E25677 /* client.cpp in Sources */, + E157FDE32AF7D1E500E25677 /* commandline.cpp in Sources */, + E157FDE42AF7D1E500E25677 /* commandloop.cpp in Sources */, + E157FDE52AF7D1E600E25677 /* config_parser.cpp in Sources */, + E157FDE62AF7D1E600E25677 /* contribute.cpp in Sources */, + E157FDE72AF7D1E600E25677 /* coremlbackend.cpp in Sources */, + E157FDEA2AF7D1E600E25677 /* datetime.cpp in Sources */, + E157FDEB2AF7D1E600E25677 /* desc.cpp in Sources */, + E157FDEC2AF7D1E600E25677 /* distributiontable.cpp in Sources */, + E157FDED2AF7D1E600E25677 /* elo.cpp in Sources */, + E157FDEE2AF7D1E600E25677 /* evalsgf.cpp in Sources */, + E157FDEF2AF7D1E600E25677 /* fancymath.cpp in Sources */, + E157FDF02AF7D1E600E25677 /* files.cpp in Sources */, + E157FDF12AF7D1E600E25677 /* fileutils.cpp in Sources */, + E157FDF22AF7D1E600E25677 /* gatekeeper.cpp in Sources */, + E157FDF32AF7D1E600E25677 /* genbook.cpp in Sources */, + E157FDF42AF7D1E600E25677 /* global.cpp in Sources */, + E157FDF52AF7D1E600E25677 /* gputest.cpp in Sources */, + E157FDF62AF7D1E600E25677 /* graphhash.cpp in Sources */, + E157FDF72AF7D1E600E25677 /* gtp.cpp in Sources */, + E12EC2342B12375C0024E274 /* writetrainingdata.cpp in Sources */, + E157FDF82AF7D1E600E25677 /* gtpconfig.cpp in Sources */, + E157FDF92AF7D1E600E25677 /* hash.cpp in Sources */, + E157FDFA2AF7D1E600E25677 /* homedata.cpp in Sources */, + E157FDFB2AF7D1E600E25677 /* loadmodel.cpp in Sources */, + E157FDFC2AF7D1E600E25677 /* localpattern.cpp in Sources */, + E157FDFD2AF7D1E600E25677 /* logger.cpp in Sources */, + E157FDFE2AF7D1E600E25677 /* main.cpp in Sources */, + E157FDFF2AF7D1E600E25677 /* mainargs.cpp in Sources */, + E157FE002AF7D1E600E25677 /* makedir.cpp in Sources */, + E157FE012AF7D1E600E25677 /* match.cpp in Sources */, + E157FE022AF7D1E600E25677 /* md5.cpp in Sources */, + E157FE032AF7D1E600E25677 /* metalbackend.cpp in Sources */, + E12EC2312B1237440024E274 /* parallel.cpp in Sources */, + E157FE052AF7D1E600E25677 /* misc.cpp in Sources */, + E157FE062AF7D1E600E25677 /* modelversion.cpp in Sources */, + E157FE072AF7D1E600E25677 /* multithread.cpp in Sources */, + E157FE082AF7D1E600E25677 /* mutexpool.cpp in Sources */, + E157FE092AF7D1E600E25677 /* nneval.cpp in Sources */, + E157FE0A2AF7D1E600E25677 /* nninputs.cpp in Sources */, + E157FE0B2AF7D1E600E25677 /* numpywrite.cpp in Sources */, + E157FE0C2AF7D1E600E25677 /* patternbonustable.cpp in Sources */, + E157FE0D2AF7D1E600E25677 /* play.cpp in Sources */, + E157FE0E2AF7D1E600E25677 /* playsettings.cpp in Sources */, + E1605CE32BFAD70100A4B872 /* sgfmetadata.cpp in Sources */, + E157FE0F2AF7D1E600E25677 /* playutils.cpp in Sources */, + E157FE102AF7D1E600E25677 /* poswriter.cpp in Sources */, + E157FE112AF7D1E600E25677 /* rand_helpers.cpp in Sources */, + E157FE122AF7D1E600E25677 /* rand.cpp in Sources */, + E157FE132AF7D1E600E25677 /* reportedsearchvalues.cpp in Sources */, + E157FE142AF7D1E600E25677 /* rules.cpp in Sources */, + E157FE152AF7D1E600E25677 /* runtests.cpp in Sources */, + E157FE162AF7D1E600E25677 /* sandbox.cpp in Sources */, + E157FE172AF7D1E600E25677 /* search.cpp in Sources */, + E157FE182AF7D1E600E25677 /* searchexplorehelpers.cpp in Sources */, + E157FE192AF7D1E600E25677 /* searchhelpers.cpp in Sources */, + E157FE1A2AF7D1E600E25677 /* searchmirror.cpp in Sources */, + E157FE1B2AF7D1E600E25677 /* searchmultithreadhelpers.cpp in Sources */, + E157FE1C2AF7D1E600E25677 /* searchnnhelpers.cpp in Sources */, + E157FE1D2AF7D1E600E25677 /* searchnode.cpp in Sources */, + E157FE1E2AF7D1E600E25677 /* searchnodetable.cpp in Sources */, + E157FE1F2AF7D1E600E25677 /* searchparams.cpp in Sources */, + E157FE202AF7D1E600E25677 /* searchresults.cpp in Sources */, + E157FE212AF7D1E600E25677 /* searchtimehelpers.cpp in Sources */, + E157FE222AF7D1E600E25677 /* searchupdatehelpers.cpp in Sources */, + E157FE232AF7D1E600E25677 /* selfplay.cpp in Sources */, + E157FE242AF7D1E600E25677 /* selfplaymanager.cpp in Sources */, + E157FE252AF7D1E600E25677 /* setup.cpp in Sources */, + E157FE262AF7D1E600E25677 /* sgf.cpp in Sources */, + E157FE272AF7D1E600E25677 /* sha2.cpp in Sources */, + E157FE282AF7D1E600E25677 /* subtreevaluebiastable.cpp in Sources */, + E157FE292AF7D1E600E25677 /* test.cpp in Sources */, + E157FE2A2AF7D1E600E25677 /* testboardarea.cpp in Sources */, + E157FE2B2AF7D1E600E25677 /* testboardbasic.cpp in Sources */, + E157FE2C2AF7D1E600E25677 /* testbook.cpp in Sources */, + E157FE2D2AF7D1E600E25677 /* testcommon.cpp in Sources */, + E157FE2E2AF7D1E600E25677 /* testconfig.cpp in Sources */, + E157FE2F2AF7D1E600E25677 /* testmisc.cpp in Sources */, + E157FE302AF7D1E600E25677 /* testnn.cpp in Sources */, + E157FE312AF7D1E600E25677 /* testnnevalcanary.cpp in Sources */, + E157FE322AF7D1E700E25677 /* testnninputs.cpp in Sources */, + E157FE332AF7D1E700E25677 /* testownership.cpp in Sources */, + E157FE342AF7D1E700E25677 /* testrules.cpp in Sources */, + E157FE352AF7D1E700E25677 /* testscore.cpp in Sources */, + E157FE362AF7D1E700E25677 /* testsearch.cpp in Sources */, + E157FE372AF7D1E700E25677 /* testsearchcommon.cpp in Sources */, + E157FE382AF7D1E700E25677 /* testsearchmisc.cpp in Sources */, + E157FE392AF7D1E700E25677 /* testsearchnonn.cpp in Sources */, + E157FE3A2AF7D1E700E25677 /* testsearchv3.cpp in Sources */, + E157FE3B2AF7D1E700E25677 /* testsearchv8.cpp in Sources */, + E157FE3C2AF7D1E700E25677 /* testsearchv9.cpp in Sources */, + E157FE3D2AF7D1E700E25677 /* testsgf.cpp in Sources */, + E157FE3E2AF7D1E700E25677 /* testsymmetries.cpp in Sources */, + E157FE3F2AF7D1E700E25677 /* testtime.cpp in Sources */, + E157FE402AF7D1E700E25677 /* testtrainingwrite.cpp in Sources */, + E157FE412AF7D1E700E25677 /* threadsafecounter.cpp in Sources */, + E157FE422AF7D1E700E25677 /* threadsafequeue.cpp in Sources */, + E157FE432AF7D1E700E25677 /* threadtest.cpp in Sources */, + E157FE442AF7D1E700E25677 /* timecontrols.cpp in Sources */, + E157FE452AF7D1E700E25677 /* timer.cpp in Sources */, + E157FE462AF7D1E700E25677 /* tinymodel.cpp in Sources */, + E157FE472AF7D1E700E25677 /* tinymodeldata.cpp in Sources */, + E157FE482AF7D1E700E25677 /* trainingwrite.cpp in Sources */, + E157FE492AF7D1E700E25677 /* tune.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E1DACF482B08997300082FF7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E12EC21E2B10D61E0024E274 /* coremlmodel.swift in Sources */, + E12EC21C2B10D61E0024E274 /* metalbackend.swift in Sources */, + E12EC21A2B10D61E0024E274 /* coremlbackend.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E1DACF5E2B089B5500082FF7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E12EC21B2B10D61E0024E274 /* coremlbackend.swift in Sources */, + E12EC21D2B10D61E0024E274 /* metalbackend.swift in Sources */, + E16BC8352C4B835F00EA3A1E /* CoreMLModelTest.swift in Sources */, + E16BC82D2C4A8AEB00EA3A1E /* ModelTest.swift in Sources */, + E1DACF652B089B5500082FF7 /* KataGoSwiftTests.swift in Sources */, + E12EC21F2B10D61E0024E274 /* coremlmodel.swift in Sources */, + E16BC82F2C4B461500EA3A1E /* CoreMLBackendTest.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + E1DACF5C2B089A4B00082FF7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E1DACF4B2B08997300082FF7 /* KataGoSwift */; + targetProxy = E1DACF5B2B089A4B00082FF7 /* PBXContainerItemProxy */; + }; + E1DACF722B089C6F00082FF7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E1DACF4B2B08997300082FF7 /* KataGoSwift */; + targetProxy = E1DACF712B089C6F00082FF7 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 21D7B48532FF4B628A950893 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + DEAD_CODE_STRIPPING = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = fast; + GCC_PREPROCESSOR_DEFINITIONS = ( + NO_GIT_REVISION, + NO_LIBZIP, + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + external, + "external/tclap-1.2.2/include", + ); + MACOSX_DEPLOYMENT_TARGET = 13.2; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ""; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OBJC_INTEROP_MODE = objcxx; + SWIFT_STRICT_CONCURRENCY = complete; + SWIFT_VERSION = 5.0; + SYSTEM_HEADER_SEARCH_PATHS = "external/filesystem-1.5.8/include"; + USE_HEADERMAP = NO; + }; + name = Release; + }; + 2E758B3F414F42EF9A6AF293 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + DEAD_CODE_STRIPPING = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + NO_GIT_REVISION, + NO_LIBZIP, + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + external, + "external/tclap-1.2.2/include", + ); + MACOSX_DEPLOYMENT_TARGET = 13.2; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ""; + SDKROOT = macosx; + SWIFT_OBJC_INTEROP_MODE = objcxx; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_STRICT_CONCURRENCY = complete; + SWIFT_VERSION = 5.0; + SYSTEM_HEADER_SEARCH_PATHS = "external/filesystem-1.5.8/include"; + USE_HEADERMAP = NO; + }; + name = Debug; + }; + 94577FBF6620419F9DEF8C32 /* MinSizeRel */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + DEAD_CODE_STRIPPING = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + NO_GIT_REVISION, + NO_LIBZIP, + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + external, + "external/tclap-1.2.2/include", + ); + MACOSX_DEPLOYMENT_TARGET = 13.2; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ""; + SDKROOT = macosx; + SWIFT_OBJC_INTEROP_MODE = objcxx; + SWIFT_STRICT_CONCURRENCY = complete; + SWIFT_VERSION = 5.0; + SYSTEM_HEADER_SEARCH_PATHS = "external/filesystem-1.5.8/include"; + USE_HEADERMAP = NO; + }; + name = MinSizeRel; + }; + DC5B919756BF4E8EA9889C99 /* RelWithDebInfo */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + DEAD_CODE_STRIPPING = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + NO_GIT_REVISION, + NO_LIBZIP, + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + external, + "external/tclap-1.2.2/include", + ); + MACOSX_DEPLOYMENT_TARGET = 13.2; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ""; + SDKROOT = macosx; + SWIFT_OBJC_INTEROP_MODE = objcxx; + SWIFT_STRICT_CONCURRENCY = complete; + SWIFT_VERSION = 5.0; + SYSTEM_HEADER_SEARCH_PATHS = "external/filesystem-1.5.8/include"; + USE_HEADERMAP = NO; + }; + name = RelWithDebInfo; + }; + E10ACAF12928A6D30004AB17 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "-"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + USE_COREML_BACKEND, + "$(inherited)", + ); + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = neuralnet/metalbridge.h; + }; + name = Debug; + }; + E10ACAF22928A6D30004AB17 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "-"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_PREPROCESSOR_DEFINITIONS = ( + USE_COREML_BACKEND, + NDEBUG, + "$(inherited)", + ); + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = neuralnet/metalbridge.h; + }; + name = Release; + }; + E10ACAF32928A6D30004AB17 /* MinSizeRel */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "-"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_PREPROCESSOR_DEFINITIONS = ( + USE_COREML_BACKEND, + "$(inherited)", + ); + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = neuralnet/metalbridge.h; + }; + name = MinSizeRel; + }; + E10ACAF42928A6D30004AB17 /* RelWithDebInfo */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "-"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_PREPROCESSOR_DEFINITIONS = ( + USE_COREML_BACKEND, + NDEBUG, + "$(inherited)", + ); + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = neuralnet/metalbridge.h; + }; + name = RelWithDebInfo; + }; + E157FDD02AF7CE2500E25677 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + USE_COREML_BACKEND, + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "$(LD_RUNPATH_SEARCH_PATHS_SHALLOW_BUNDLE_$(SHALLOW_BUNDLE))", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = ccy.katagotest; + PRODUCT_MODULE_NAME = katago; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + E157FDD12AF7CE2500E25677 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + USE_COREML_BACKEND, + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "$(LD_RUNPATH_SEARCH_PATHS_SHALLOW_BUNDLE_$(SHALLOW_BUNDLE))", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = ccy.katagotest; + PRODUCT_MODULE_NAME = katago; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + E157FDD22AF7CE2500E25677 /* MinSizeRel */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + USE_COREML_BACKEND, + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "$(LD_RUNPATH_SEARCH_PATHS_SHALLOW_BUNDLE_$(SHALLOW_BUNDLE))", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = ccy.katagotest; + PRODUCT_MODULE_NAME = katago; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + }; + name = MinSizeRel; + }; + E157FDD32AF7CE2500E25677 /* RelWithDebInfo */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + USE_COREML_BACKEND, + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "$(LD_RUNPATH_SEARCH_PATHS_SHALLOW_BUNDLE_$(SHALLOW_BUNDLE))", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = ccy.katagotest; + PRODUCT_MODULE_NAME = katago; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + }; + name = RelWithDebInfo; + }; + E1DACF502B08997400082FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 13.2; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = ccy.KataGoSwift; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + E1DACF512B08997400082FF7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 13.2; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = ccy.KataGoSwift; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + E1DACF522B08997400082FF7 /* MinSizeRel */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 13.2; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = ccy.KataGoSwift; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = MinSizeRel; + }; + E1DACF532B08997400082FF7 /* RelWithDebInfo */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 13.2; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = ccy.KataGoSwift; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = RelWithDebInfo; + }; + E1DACF6A2B089B5500082FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 13.2; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = ccy.KataGoSwiftTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + E1DACF6B2B089B5500082FF7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 13.2; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = ccy.KataGoSwiftTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + E1DACF6C2B089B5500082FF7 /* MinSizeRel */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 13.2; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = ccy.KataGoSwiftTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + }; + name = MinSizeRel; + }; + E1DACF6D2B089B5500082FF7 /* RelWithDebInfo */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 13.2; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = ccy.KataGoSwiftTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + }; + name = RelWithDebInfo; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0838DC7C409844AFA516AAE2 /* Build configuration list for PBXProject "KataGo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2E758B3F414F42EF9A6AF293 /* Debug */, + 21D7B48532FF4B628A950893 /* Release */, + 94577FBF6620419F9DEF8C32 /* MinSizeRel */, + DC5B919756BF4E8EA9889C99 /* RelWithDebInfo */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E10ACAF02928A6D30004AB17 /* Build configuration list for PBXNativeTarget "katago" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E10ACAF12928A6D30004AB17 /* Debug */, + E10ACAF22928A6D30004AB17 /* Release */, + E10ACAF32928A6D30004AB17 /* MinSizeRel */, + E10ACAF42928A6D30004AB17 /* RelWithDebInfo */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E157FDD42AF7CE2500E25677 /* Build configuration list for PBXNativeTarget "katagotest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E157FDD02AF7CE2500E25677 /* Debug */, + E157FDD12AF7CE2500E25677 /* Release */, + E157FDD22AF7CE2500E25677 /* MinSizeRel */, + E157FDD32AF7CE2500E25677 /* RelWithDebInfo */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E1DACF542B08997400082FF7 /* Build configuration list for PBXNativeTarget "KataGoSwift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E1DACF502B08997400082FF7 /* Debug */, + E1DACF512B08997400082FF7 /* Release */, + E1DACF522B08997400082FF7 /* MinSizeRel */, + E1DACF532B08997400082FF7 /* RelWithDebInfo */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E1DACF692B089B5500082FF7 /* Build configuration list for PBXNativeTarget "KataGoSwiftTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E1DACF6A2B089B5500082FF7 /* Debug */, + E1DACF6B2B089B5500082FF7 /* Release */, + E1DACF6C2B089B5500082FF7 /* MinSizeRel */, + E1DACF6D2B089B5500082FF7 /* RelWithDebInfo */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 91644CF2108748368B902DCE /* Project object */; +} diff --git a/cpp/xcode/KataGo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/cpp/xcode/KataGo.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/cpp/xcode/KataGo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/cpp/xcode/KataGo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/cpp/xcode/KataGo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/cpp/xcode/KataGo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/cpp/xcode/KataGo.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/cpp/xcode/KataGo.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..530b83358 --- /dev/null +++ b/cpp/xcode/KataGo.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,10 @@ + + + + + BuildSystemType + Latest + PreviewsEnabled + + + diff --git a/cpp/xcode/KataGo.xcodeproj/xcshareddata/xcschemes/katago.xcscheme b/cpp/xcode/KataGo.xcodeproj/xcshareddata/xcschemes/katago.xcscheme new file mode 100644 index 000000000..f6254c9a4 --- /dev/null +++ b/cpp/xcode/KataGo.xcodeproj/xcshareddata/xcschemes/katago.xcscheme @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cpp/xcode/KataGoSwiftTests/CoreMLBackendTest.swift b/cpp/xcode/KataGoSwiftTests/CoreMLBackendTest.swift new file mode 100644 index 000000000..0aa1f79f2 --- /dev/null +++ b/cpp/xcode/KataGoSwiftTests/CoreMLBackendTest.swift @@ -0,0 +1,72 @@ +// +// CoreMLBackendTest.swift +// KataGoSwiftTests +// +// Created by Chin-Chang Yang on 2024/7/20. +// + +import XCTest + +final class CoreMLBackendTest: XCTestCase { + + func testNilCoreMLBackend() { + let backend = maybeCreateCoreMLBackend(xLen: 1, + yLen: 1) + + XCTAssertNil(backend) + } + + func testCoreMLBackendMeta() { + let backend = maybeCreateCoreMLBackend(metaEncoderVersion: 1, + useCpuAndNeuralEngine: false)! + + checkBackendOutput(backend: backend) + } + + func testCoreMLBackendMetaNE() { + let backend = maybeCreateCoreMLBackend(metaEncoderVersion: 1, + useCpuAndNeuralEngine: true)! + + checkBackendOutput(backend: backend) + } + + func checkBackendOutput(backend: CoreMLBackend) { + var binInputs = [Float32](repeating: 1, count: backend.spatialSize) + var globalInputs = [Float32](repeating: 1, count: backend.numGlobalFeatures) + var metaInputs = [Float32](repeating: 1, count: backend.numMetaFeatures) + // See the contents in Predictions tab of a mlpackage file + let policyOutputsSize = 1 * 6 * 362 + let valueOutputsSize = 1 * 3 + let ownershipOutputsSize = 1 * 1 * 19 * 19 + let miscValuesOutputsSize = 1 * 10 + let moreMiscValuesOutputsSize = 1 * 8 + var policyOutputs = [Float32](repeating: 1, count: policyOutputsSize) + var valueOutputs = [Float32](repeating: 1, count: valueOutputsSize) + var ownershipOutputs = [Float32](repeating: 1, count: ownershipOutputsSize) + var miscValuesOutputs = [Float32](repeating: 1, count: miscValuesOutputsSize) + var moreMiscValuesOutputs = [Float32](repeating: 1, count: moreMiscValuesOutputsSize) + let batchSize = 1 + + backend.getBatchOutput(binInputs: &binInputs, + globalInputs: &globalInputs, + metaInputs: &metaInputs, + policyOutputs: &policyOutputs, + valueOutputs: &valueOutputs, + ownershipOutputs: &ownershipOutputs, + miscValuesOutputs: &miscValuesOutputs, + moreMiscValuesOutputs: &moreMiscValuesOutputs, + batchSize: batchSize) + + XCTAssertEqual(policyOutputs[0], -14.86533, accuracy: 1e-3) + XCTAssertEqual(policyOutputs[policyOutputsSize - 1], -4.618265, accuracy: 1e-3) + XCTAssertEqual(valueOutputs[0], -2.6803048, accuracy: 1e-3) + XCTAssertEqual(valueOutputs[valueOutputsSize - 1], -10.766384, accuracy: 1e-3) + XCTAssertEqual(ownershipOutputs[0], -0.05757516, accuracy: 1e-3) + XCTAssertEqual(ownershipOutputs[ownershipOutputsSize - 1], -0.08216501, accuracy: 1e-3) + XCTAssertEqual(miscValuesOutputs[0], -15.050129, accuracy: 1e-3) + XCTAssertEqual(miscValuesOutputs[miscValuesOutputsSize - 1], -8.116809, accuracy: 1e-3) + XCTAssertEqual(moreMiscValuesOutputs[0], -4.365787, accuracy: 1e-3) + XCTAssertEqual(moreMiscValuesOutputs[moreMiscValuesOutputsSize - 1], -20.357615, accuracy: 1e-3) + + } +} diff --git a/cpp/xcode/KataGoSwiftTests/CoreMLModelTest.swift b/cpp/xcode/KataGoSwiftTests/CoreMLModelTest.swift new file mode 100644 index 000000000..af0a0a1e7 --- /dev/null +++ b/cpp/xcode/KataGoSwiftTests/CoreMLModelTest.swift @@ -0,0 +1,19 @@ +// +// CoreMLModelTest.swift +// KataGoSwiftTests +// +// Created by Chin-Chang Yang on 2024/7/20. +// + +import XCTest + +final class CoreMLModelTest: XCTestCase { + func testFreshCompileBundleMLModel() { + let modelName = CoreMLBackend.getModelName() + + let mlmodel = KataGoModel.compileBundleMLModel(modelName: modelName, + computeUnits: .cpuAndNeuralEngine) + + XCTAssertNotNil(mlmodel) + } +} diff --git a/cpp/xcode/KataGoSwiftTests/KataGoSwiftTests.swift b/cpp/xcode/KataGoSwiftTests/KataGoSwiftTests.swift new file mode 100644 index 000000000..8d2f404b5 --- /dev/null +++ b/cpp/xcode/KataGoSwiftTests/KataGoSwiftTests.swift @@ -0,0 +1,2200 @@ +import XCTest +import MetalPerformanceShadersGraph + +extension MPSNDArray { + /// Returns the total number of elements in the MPSNDArray. + func countElements() -> Int { + // Initialize the range of dimensions from 0 to numberOfDimensions - 1 + let dimensionsRange = 0...allocate(capacity: 5) + + inputPointer[0] = -1 + inputPointer[1] = 0 + inputPointer[2] = 1 + inputPointer[3] = 10.38 + inputPointer[4] = 10.4 + + let inputDescriptor = MPSNDArrayDescriptor(dataType: inputTensor.dataType, + shape: shape) + + let inputArray = MPSNDArray(device: device, + descriptor: inputDescriptor) + + inputArray.writeBytes(inputPointer) + + let inputTensorData = MPSGraphTensorData(inputArray) + + let fetch = graph.run(feeds: [inputTensor: inputTensorData], + targetTensors: [mishTensor], + targetOperations: nil) + + let length = shape.countElements() + let buffer = UnsafeMutablePointer.allocate(capacity: length) + + fetch[mishTensor]?.mpsndarray().readBytes(buffer) + + XCTAssert(mishTensor.shape == shape) + XCTAssertEqual(buffer[0], -0.30340147018432617, accuracy: 1e-6) + XCTAssertEqual(buffer[1], 0.0, accuracy: 1e-6) + XCTAssertEqual(buffer[2], 0.8650983572006226, accuracy: 1e-6) + XCTAssertEqual(buffer[3], 10.380000114440918, accuracy: 1e-6) + XCTAssertEqual(buffer[4], 10.4, accuracy: 1e-6) + } +} + +final class InputLayerTest: XCTestCase { + + func testNCHW() { + let sourceLayer = InputLayer(graph: MPSGraph(), + nnXLen: 5, + nnYLen: 4, + numChannels: 3) + + XCTAssert(sourceLayer.tensor.shape == [-1, 3, 4, 5]) + XCTAssert(sourceLayer.tensor.dataType == .float32) + } +} + +final class InputGlobalLayerTest: XCTestCase { + + func testNilTensor() { + let inputGlobalLayer = InputGlobalLayer(graph: MPSGraph(), + numGlobalFeatures: 3) + + XCTAssert(inputGlobalLayer.tensor.shape == [-1, 3, 1, 1]) + XCTAssert(inputGlobalLayer.tensor.dataType == .float32) + } +} + +final class MaskLayerTest: XCTestCase { + + func testNilTensor() { + let graph = MPSGraph() + + let maskLayer = MaskLayer(graph: graph, + nnXLen: 4, + nnYLen: 3) + + XCTAssert(maskLayer.tensor.shape == [-1, 1, 3, 4]) + XCTAssert(maskLayer.tensor.dataType == .float32) + } +} + +final class MaskSumLayerTest: XCTestCase { + + func testTensor() { + let graph = MPSGraph() + let shape: [NSNumber] = [2, 1, 1, 1] + let tensor = graph.constant(12, shape: shape, dataType: .float32) + let maskSumLayer = MaskSumLayer(tensor: tensor) + + let fetch = graph.run(feeds: [:], + targetTensors: [maskSumLayer.tensor], + targetOperations: nil) + + let length = shape.countElements() + let buffer = UnsafeMutablePointer.allocate(capacity: length) + + fetch[maskSumLayer.tensor]?.mpsndarray().readBytes(buffer) + + XCTAssert(maskSumLayer.tensor.shape == [2, 1, 1, 1]) + XCTAssertEqual(buffer[0], 12) + XCTAssertEqual(buffer[1], 12) + } + + func testNilTensor() { + let graph = MPSGraph() + let shape: [NSNumber] = [2, 1, 3, 4] + let tensor = graph.constant(1, shape: shape, dataType: .float32) + + let maskSumLayer = MaskSumLayer(graph: graph, + maskTensor: tensor) + + XCTAssert(maskSumLayer.tensor.shape == [2, 1, 1, 1]) + + let fetch = graph.run(feeds: [:], + targetTensors: [maskSumLayer.tensor], + targetOperations: nil) + + let length = shape.countElements() + let buffer = UnsafeMutablePointer.allocate(capacity: length) + + fetch[maskSumLayer.tensor]?.mpsndarray().readBytes(buffer) + + XCTAssertEqual(buffer[0], 12) + XCTAssertEqual(buffer[1], 12) + } +} + +final class MaskSumSqrtS14M01LayerTest: XCTestCase { + + func testTensor() { + let graph = MPSGraph() + let shape: [NSNumber] = [2, 1, 1, 1] + + let tensor = graph.constant(-1.053589838486225, + shape: shape, + dataType: .float32) + + let maskSumSqrtS14M01Layer = MaskSumSqrtS14M01Layer(tensor: tensor) + + let fetch = graph.run(feeds: [:], + targetTensors: [maskSumSqrtS14M01Layer.tensor], + targetOperations: nil) + + let length = shape.countElements() + let buffer = UnsafeMutablePointer.allocate(capacity: length) + + fetch[maskSumSqrtS14M01Layer.tensor]?.mpsndarray().readBytes(buffer) + + XCTAssert(maskSumSqrtS14M01Layer.tensor.shape == [2, 1, 1, 1]) + XCTAssertEqual(buffer[0], -1.053589838486225, accuracy: 1e-8) + XCTAssertEqual(buffer[1], -1.053589838486225, accuracy: 1e-8) + } + + func testNilTensor() { + let graph = MPSGraph() + + let shape: [NSNumber] = [2, 1, 3, 4] + + let tensor = graph.constant(1, + shape: shape, + dataType: .float32) + + let maskSumLayer = MaskSumLayer(graph: graph, + maskTensor: tensor) + + let maskSumSqrtS14M01Layer = MaskSumSqrtS14M01Layer(graph: graph, + maskSum: maskSumLayer) + + let fetch = graph.run(feeds: [:], + targetTensors: [maskSumSqrtS14M01Layer.tensor], + targetOperations: nil) + + let length = shape.countElements() + let buffer = UnsafeMutablePointer.allocate(capacity: length) + + fetch[maskSumSqrtS14M01Layer.tensor]?.mpsndarray().readBytes(buffer) + + XCTAssert(maskSumSqrtS14M01Layer.tensor.shape == [2, 1, 1, 1]) + XCTAssertEqual(buffer[0], -1.053589838486225, accuracy: 1e-8) + XCTAssertEqual(buffer[1], -1.053589838486225, accuracy: 1e-8) + } +} + +final class MaskSumSqrtS14M01SquareS01LayerTest: XCTestCase { + + func testTensor() { + let graph = MPSGraph() + let shape: [NSNumber] = [2, 1, 1, 1] + + let tensor = graph.constant(1.010051547761429, + shape: shape, + dataType: .float32) + + let maskSumSqrtS14M01SquareS01Layer = MaskSumSqrtS14M01SquareS01Layer(tensor: tensor) + + let fetch = graph.run(feeds: [:], + targetTensors: [maskSumSqrtS14M01SquareS01Layer.tensor], + targetOperations: nil) + + let length = shape.countElements() + let buffer = UnsafeMutablePointer.allocate(capacity: length) + + fetch[maskSumSqrtS14M01SquareS01Layer.tensor]?.mpsndarray().readBytes(buffer) + + XCTAssert(maskSumSqrtS14M01SquareS01Layer.tensor.shape == [2, 1, 1, 1]) + XCTAssertEqual(buffer[0], 1.010051547761429, accuracy: 1e-8) + XCTAssertEqual(buffer[1], 1.010051547761429, accuracy: 1e-8) + } + + func testNilTensor() { + let graph = MPSGraph() + let shape: [NSNumber] = [2, 1, 3, 4] + + let tensor = graph.constant(1, + shape: shape, + dataType: .float32) + + let maskSumLayer = MaskSumLayer(graph: graph, + maskTensor: tensor) + + let maskSumSqrtS14M01Layer = MaskSumSqrtS14M01Layer(graph: graph, + maskSum: maskSumLayer) + + let maskSumSqrtS14M01SquareS01Layer = + MaskSumSqrtS14M01SquareS01Layer(graph: graph, + maskSumSqrtS14M01: maskSumSqrtS14M01Layer) + + let fetch = graph.run(feeds: [:], + targetTensors: [maskSumSqrtS14M01SquareS01Layer.tensor], + targetOperations: nil) + + let length = shape.countElements() + let buffer = UnsafeMutablePointer.allocate(capacity: length) + + fetch[maskSumSqrtS14M01SquareS01Layer.tensor]?.mpsndarray().readBytes(buffer) + + XCTAssert(maskSumSqrtS14M01SquareS01Layer.tensor.shape == [2, 1, 1, 1]) + XCTAssertEqual(buffer[0], 1.010051547761429, accuracy: 1e-8) + XCTAssertEqual(buffer[1], 1.010051547761429, accuracy: 1e-8) + } +} + +final class ConvLayerTest: XCTestCase { + + func testBase() { + let convXSize = 3 + let convYSize = 3 + let outChannels: NSNumber = 2 + let weightsLength = convXSize * convYSize * outChannels.intValue + let weights = UnsafeMutablePointer.allocate(capacity: weightsLength) + + weights[0] = 0 + weights[1] = 1 + weights[2] = 0 + weights[3] = 0 + weights[4] = 0 + weights[5] = 0 + weights[6] = 0 + weights[7] = 0 + weights[8] = 0 + + weights[9] = 0 + weights[10] = 0 + weights[11] = 0 + weights[12] = 0 + weights[13] = 0 + weights[14] = 0 + weights[15] = 0 + weights[16] = 1 + weights[17] = 0 + + let inChannels: NSNumber = 1 + + let descriptor = createSWConvLayerDesc(convYSize: Int32(convYSize), + convXSize: Int32(convXSize), + inChannels: Int32(truncating: inChannels), + outChannels: Int32(truncating: outChannels), + dilationY: 1, + dilationX: 1, + weights: weights) + + let batchSize: NSNumber = 1 + let nnXLen: NSNumber = 3 + let nnYLen: NSNumber = 2 + + let inputLength = batchSize.intValue * nnXLen.intValue * nnYLen.intValue * inChannels.intValue + + let inputPointer = UnsafeMutablePointer.allocate(capacity: inputLength) + + inputPointer[0] = 0 + inputPointer[1] = 1 + inputPointer[2] = 2 + inputPointer[3] = 3 + inputPointer[4] = 4 + inputPointer[5] = 5 + + let outputLength = batchSize.intValue * nnXLen.intValue * nnYLen.intValue * outChannels.intValue + + let outputPointer = UnsafeMutablePointer.allocate(capacity: outputLength) + + testConvLayer(descriptor: descriptor, + nnXLen: Int32(truncating: nnXLen), + nnYLen: Int32(truncating: nnYLen), + batchSize: Int32(truncating: batchSize), + input: inputPointer, + output: outputPointer) + + XCTAssertEqual(outputPointer[0], 0, accuracy: 1e-8) + XCTAssertEqual(outputPointer[2], 0, accuracy: 1e-8) + XCTAssertEqual(outputPointer[4], 1, accuracy: 1e-8) + XCTAssertEqual(outputPointer[6], 3, accuracy: 1e-8) + XCTAssertEqual(outputPointer[8], 5, accuracy: 1e-8) + XCTAssertEqual(outputPointer[10], 0, accuracy: 1e-8) + + XCTAssertEqual(outputPointer[1], 0, accuracy: 1e-8) + XCTAssertEqual(outputPointer[3], 0, accuracy: 1e-8) + XCTAssertEqual(outputPointer[5], 2, accuracy: 1e-8) + XCTAssertEqual(outputPointer[7], 4, accuracy: 1e-8) + XCTAssertEqual(outputPointer[9], 0, accuracy: 1e-8) + XCTAssertEqual(outputPointer[11], 0, accuracy: 1e-8) + } +} + +final class BatchNormLayerTest: XCTestCase { + + func testBase() { + let numChannels: NSNumber = 2 + let mean: [Float] = [0, 2] + let variance: [Float] = [3.9, 0.15] + let epsilon: Float = 0.1 + let scale: [Float] = [0.1, 1] + let bias: [Float] = [10, 0] + + var mergedScale = SWBatchNormLayerDesc.mergeScales(scaleWeights: scale, + varianceWeights: variance, + epsilon: epsilon) + + var mergedBias = SWBatchNormLayerDesc.mergedBiases(biasWeights: bias, + meanWeights: mean, + mergedScales: mergedScale) + + let descriptor = createSWBatchNormLayerDesc(numChannels: numChannels.int32Value, + mergedScale: &mergedScale, + mergedBias: &mergedBias) + + let batchSize: NSNumber = 2 + let nnXLen: NSNumber = 5 + let nnYLen: NSNumber = 2 + + let inputLength = batchSize.intValue * nnXLen.intValue * nnYLen.intValue * numChannels.intValue + + let inputPointer = UnsafeMutablePointer.allocate(capacity: inputLength) + let x = inputPointer + + x[0] = 5; x[2] = 5; x[4] = 4; x[6] = 4; x[8] = 9 + x[10] = 1; x[12] = 1; x[14] = 8; x[16] = 8; x[18] = 9 + + x[1] = 0; x[3] = 1; x[5] = 2; x[7] = 3; x[9] = 4 + x[11] = 8; x[13] = 7; x[15] = 6; x[17] = 5; x[19] = 4 + + x[20] = 3; x[22] = 0; x[24] = 4; x[26] = 0; x[28] = 5 + x[30] = 0; x[32] = 5; x[34] = 0; x[36] = 6; x[38] = 0 + + x[21] = 1; x[23] = 0; x[25] = 0; x[27] = 2; x[29] = 1 + x[31] = 0; x[33] = 2; x[35] = 2; x[37] = 0; x[39] = 2 + + let maskLength = batchSize.intValue * nnXLen.intValue * nnYLen.intValue + let maskPointer = UnsafeMutablePointer.allocate(capacity: maskLength) + let m = maskPointer + + m[0] = 1; m[1] = 1; m[2] = 1; m[3] = 1; m[4] = 1 + m[5] = 1; m[6] = 1; m[7] = 1; m[8] = 1; m[9] = 1 + + m[10] = 1; m[11] = 1; m[12] = 1; m[13] = 1; m[14] = 1 + m[15] = 1; m[16] = 1; m[17] = 1; m[18] = 1; m[19] = 1 + + let outputLength = batchSize.intValue * nnXLen.intValue * nnYLen.intValue * numChannels.intValue + + let outputPointer = UnsafeMutablePointer.allocate(capacity: outputLength) + + testBatchNormLayer(descriptor: descriptor, + nnXLen: Int32(truncating: nnXLen), + nnYLen: Int32(truncating: nnYLen), + batchSize: Int32(truncating: batchSize), + input: inputPointer, + mask: maskPointer, + output: outputPointer) + + XCTAssertEqual(outputPointer[0], 10.25, accuracy: 1e-8) + XCTAssertEqual(outputPointer[8], 10.45, accuracy: 1e-8) + XCTAssertEqual(outputPointer[10], -2.0, accuracy: 1e-8) + XCTAssertEqual(outputPointer[18], 14.0, accuracy: 1e-8) + XCTAssertEqual(outputPointer[19], 4, accuracy: 1e-8) + XCTAssertEqual(outputPointer[20], 10.15, accuracy: 1e-8) + XCTAssertEqual(outputPointer[39], 0, accuracy: 1e-8) + } +} + +final class ActivationLayerTest: XCTestCase { + + func testMish() { + let device = MTLCreateSystemDefaultDevice()! + let graph = MPSGraph() + let inputNumber = 6 + let shape: [NSNumber] = [NSNumber(value: inputNumber)] + let inputTensor = graph.placeholder(shape: shape, name: nil) + + let activationLayer = ActivationLayer(graph: graph, + sourceTensor: inputTensor, + activationKind: ActivationKind.mish) + + let inputPointer = UnsafeMutablePointer.allocate(capacity: inputNumber) + + inputPointer[0] = -1e10 + inputPointer[1] = -1 + inputPointer[2] = 0 + inputPointer[3] = 1 + inputPointer[4] = 10.38 + inputPointer[5] = 1e10 + + let inputDescriptor = MPSNDArrayDescriptor(dataType: inputTensor.dataType, + shape: shape) + + let inputArray = MPSNDArray(device: device, + descriptor: inputDescriptor) + + inputArray.writeBytes(inputPointer) + let inputTensorData = MPSGraphTensorData(inputArray) + + let fetch = graph.run(feeds: [inputTensor: inputTensorData], + targetTensors: [activationLayer.resultTensor], + targetOperations: nil) + + let length = shape.countElements() + let buffer = UnsafeMutablePointer.allocate(capacity: length) + + fetch[activationLayer.resultTensor]?.mpsndarray().readBytes(buffer) + + XCTAssert(activationLayer.resultTensor.shape == shape) + XCTAssertEqual(buffer[0], 0.0, accuracy: 1e-6) + XCTAssertEqual(buffer[1], -0.30340147018432617, accuracy: 1e-6) + XCTAssertEqual(buffer[2], 0.0, accuracy: 1e-6) + XCTAssertEqual(buffer[3], 0.8650983572006226, accuracy: 1e-6) + XCTAssertEqual(buffer[4], 10.380000114440918, accuracy: 1e-6) + XCTAssertEqual(buffer[5], 1e10, accuracy: 1e4) + } + + func testIdentity() { + let device = MTLCreateSystemDefaultDevice()! + let graph = MPSGraph() + let shape: [NSNumber] = [5] + let inputTensor = graph.placeholder(shape: shape, name: nil) + + let activationLayer = ActivationLayer(graph: graph, + sourceTensor: inputTensor, + activationKind: ActivationKind.identity) + + let inputPointer = UnsafeMutablePointer.allocate(capacity: 5) + + inputPointer[0] = -10.38 + inputPointer[1] = -1 + inputPointer[2] = 0 + inputPointer[3] = 1 + inputPointer[4] = 10.38 + + let inputDescriptor = MPSNDArrayDescriptor(dataType: inputTensor.dataType, + shape: shape) + + let inputArray = MPSNDArray(device: device, + descriptor: inputDescriptor) + + inputArray.writeBytes(inputPointer) + let inputTensorData = MPSGraphTensorData(inputArray) + + let fetch = graph.run(feeds: [inputTensor: inputTensorData], + targetTensors: [activationLayer.resultTensor], + targetOperations: nil) + + let length = shape.countElements() + let buffer = UnsafeMutablePointer.allocate(capacity: length) + + fetch[activationLayer.resultTensor]?.mpsndarray().readBytes(buffer) + + XCTAssert(activationLayer.resultTensor.shape == shape) + XCTAssertEqual(buffer[0], inputPointer[0], accuracy: 1e-6) + XCTAssertEqual(buffer[1], inputPointer[1], accuracy: 1e-6) + XCTAssertEqual(buffer[2], inputPointer[2], accuracy: 1e-6) + XCTAssertEqual(buffer[3], inputPointer[3], accuracy: 1e-6) + XCTAssertEqual(buffer[4], inputPointer[4], accuracy: 1e-6) + } +} + +final class ResidualBlockTest: XCTestCase { + + func testNHWC() { + let batchSize: NSNumber = 2 + let trunkChannels: NSNumber = 1 + let midChannels: NSNumber = 2 + let nnYLen: NSNumber = 3 + let nnXLen: NSNumber = 4 + + let inputLength = batchSize.intValue * nnXLen.intValue * nnYLen.intValue * trunkChannels.intValue + + let inputPointer = UnsafeMutablePointer.allocate(capacity: inputLength) + let x = inputPointer + + x[0] = 1; x[1] = 0; x[2] = 0; x[3] = 0 + x[4] = 0; x[5] = 2; x[6] = 2; x[7] = 0 + x[8] = 0; x[9] = 0; x[10] = 0; x[11] = 1 + + x[12] = 0; x[13] = 0; x[14] = 0; x[15] = 0 + x[16] = 0; x[17] = 3; x[18] = -5; x[19] = 0 + x[20] = 1; x[21] = 1; x[22] = 1; x[23] = 1 + + let maskLength = batchSize.intValue * nnXLen.intValue * nnYLen.intValue + let maskPointer = UnsafeMutablePointer.allocate(capacity: maskLength) + let m = maskPointer + + m[0] = 1; m[1] = 1; m[2] = 0; m[3] = 1 + m[4] = 1; m[5] = 1; m[6] = 1; m[7] = 1 + m[8] = 1; m[9] = 1; m[10] = 0; m[11] = 1 + + m[12] = 1; m[13] = 1; m[14] = 1; m[15] = 1 + m[16] = 1; m[17] = 1; m[18] = 1; m[19] = 0 + m[20] = 1; m[21] = 1; m[22] = 1; m[23] = 1 + + let preBN_mean: [Float] = [0] + let preBN_variance: [Float] = [0.9] + let preBN_epsilon: Float = 0.1 + let preBN_scale: [Float] = [2] + let preBN_bias: [Float] = [0] + + var preBN_mergedScale = SWBatchNormLayerDesc.mergeScales(scaleWeights: preBN_scale, + varianceWeights: preBN_variance, + epsilon: preBN_epsilon) + + var preBN_mergedBias = SWBatchNormLayerDesc.mergedBiases(biasWeights: preBN_bias, + meanWeights: preBN_mean, + mergedScales: preBN_mergedScale) + + let preBN = SWBatchNormLayerDesc(numChannels: trunkChannels, + mergedScale: &preBN_mergedScale, + mergedBias: &preBN_mergedBias) + + let convYSize: NSNumber = 3 + let convXSize: NSNumber = 3 + let capacity = convYSize.intValue * convXSize.intValue * midChannels.intValue + + let regularConv = SWConvLayerDesc(convYSize: convYSize, + convXSize: convXSize, + inChannels: trunkChannels, + outChannels: midChannels, + dilationY: 1, + dilationX: 1, + weights: UnsafeMutablePointer.allocate(capacity: capacity)) + + let w = regularConv.weights; + + w[0] = 0; w[1] = 1; w[2] = 0 + w[3] = 0; w[4] = 0; w[5] = 0 + w[6] = 0; w[7] = 0; w[8] = 0 + + w[9] = 0; w[10] = 0; w[11] = 0 + w[12] = 0; w[13] = 0; w[14] = 0 + w[15] = 0; w[16] = 1; w[17] = 0 + + let midBN_mean: [Float] = [3, 0] + let midBN_variance: [Float] = [0.9, 0.9] + let midBN_epsilon: Float = 0.1 + let midBN_scale: [Float] = [1, 1] + let midBN_bias: [Float] = [0, 0] + + var midBN_mergedScale = SWBatchNormLayerDesc.mergeScales(scaleWeights: midBN_scale, + varianceWeights: midBN_variance, + epsilon: midBN_epsilon) + + var midBN_mergedBias = SWBatchNormLayerDesc.mergedBiases(biasWeights: midBN_bias, + meanWeights: midBN_mean, + mergedScales: midBN_mergedScale) + + let midBN = SWBatchNormLayerDesc(numChannels: midChannels, + mergedScale: &midBN_mergedScale, + mergedBias: &midBN_mergedBias) + + let finalConv = SWConvLayerDesc(convYSize: 1, + convXSize: 1, + inChannels: midChannels, + outChannels: trunkChannels, + dilationY: 1, + dilationX: 1, + weights: UnsafeMutablePointer.allocate(capacity: 2)) + + finalConv.weights[0] = 1; finalConv.weights[1] = 1 + + let descriptor = createSWResidualBlockDesc(preBN: preBN, + preActivation: ActivationKind.relu, + regularConv: regularConv, + midBN: midBN, + midActivation: ActivationKind.relu, + finalConv: finalConv) + + let outputLength = batchSize.intValue * trunkChannels.intValue * nnYLen.intValue * nnXLen.intValue + + let outputPointer = UnsafeMutablePointer.allocate(capacity: outputLength) + + testResidualBlock(descriptor: descriptor, + batchSize: Int32(truncating: batchSize), + nnXLen: Int32(truncating: nnXLen), + nnYLen: Int32(truncating: nnYLen), + input: inputPointer, + mask: maskPointer, + output: outputPointer) + + XCTAssertEqual(outputPointer[0], 1, accuracy: 1e-8) + XCTAssertEqual(outputPointer[3], 0, accuracy: 1e-8) + XCTAssertEqual(outputPointer[4], 0, accuracy: 1e-8) + XCTAssertEqual(outputPointer[11], 1, accuracy: 1e-8) + XCTAssertEqual(outputPointer[12], 0, accuracy: 1e-8) + XCTAssertEqual(outputPointer[18], -3, accuracy: 1e-8) + XCTAssertEqual(outputPointer[23], 1, accuracy: 1e-8) + } + + func testUnity() { + let batchSize = 2 + let nnXLen = 2 + let nnYLen = 2 + let numChannels = 2 + + let unityConvWeights = UnsafeMutablePointer.allocate(capacity: numChannels * numChannels) + + unityConvWeights[0] = 1 + unityConvWeights[1] = 0 + unityConvWeights[2] = 0 + unityConvWeights[3] = 1 + + let unityConv = SWConvLayerDesc(convYSize: 1, + convXSize: 1, + inChannels: numChannels as NSNumber, + outChannels: numChannels as NSNumber, + dilationY: 1, + dilationX: 1, + weights: unityConvWeights) + + let unityBN_mean: [Float] = [0, 0] + let unityBN_variance: [Float] = [0.9, 0.9] + let unityBN_epsilon: Float = 0.1 + let unityBN_scale: [Float] = [1, 1] + let unityBN_bias: [Float] = [0, 0] + + var unityBN_mergedScale = SWBatchNormLayerDesc.mergeScales(scaleWeights: unityBN_scale, + varianceWeights: unityBN_variance, + epsilon: unityBN_epsilon) + + var unityBN_mergedBias = SWBatchNormLayerDesc.mergedBiases(biasWeights: unityBN_bias, + meanWeights: unityBN_mean, + mergedScales: unityBN_mergedScale) + + let unityBN = SWBatchNormLayerDesc(numChannels: numChannels as NSNumber, + mergedScale: &unityBN_mergedScale, + mergedBias: &unityBN_mergedBias) + + let residualBlock = SWResidualBlockDesc(preBN: unityBN, + preActivation: ActivationKind.relu, + regularConv: unityConv, + midBN: unityBN, + midActivation: ActivationKind.relu, + finalConv: unityConv) + + let graph = MPSGraph() + + let input = InputLayer(graph: graph, + nnXLen: nnXLen as NSNumber, + nnYLen: nnYLen as NSNumber, + numChannels: numChannels as NSNumber) + + let mask = MaskLayer(graph: graph, + nnXLen: nnXLen as NSNumber, + nnYLen: nnYLen as NSNumber) + + let block = ResidualBlock(graph: graph, + sourceTensor: input.tensor, + maskTensor: mask.tensor, + descriptor: residualBlock, + nnXLen: nnXLen as NSNumber, + nnYLen: nnYLen as NSNumber) + + let inputCount = batchSize * numChannels * nnXLen * nnYLen + let inputPointer = UnsafeMutablePointer.allocate(capacity: inputCount) + + for i in 0...allocate(capacity: maskCount) + + for i in 0...allocate(capacity: inputCount) + + fetch[block.resultTensor]?.mpsndarray().readBytes(outputPointer) + + XCTAssertEqual(outputPointer[0], 0, accuracy: 1e-8) + XCTAssertEqual(outputPointer[1], 2, accuracy: 1e-8) + XCTAssertEqual(outputPointer[2], 4, accuracy: 1e-8) + XCTAssertEqual(outputPointer[3], 6, accuracy: 1e-8) + XCTAssertEqual(outputPointer[15], 30, accuracy: 1e-8) + } +} + +final class GlobalPoolingResidualBlockTest: XCTestCase { + + func testNHWC() { + let batchSize: NSNumber = 2 + let trunkChannels: NSNumber = 1 + let regularChannels: NSNumber = 1 + let gpoolChannels: NSNumber = 2 + let nnYLen: NSNumber = 3 + let nnXLen: NSNumber = 4 + + let inputPointer = UnsafeMutablePointer.allocate(capacity: 24) + let x = inputPointer + + x[0] = 1; x[1] = 2; x[2] = 0; x[3] = 0 + x[4] = 0; x[5] = 3; x[6] = 4; x[7] = 0 + x[8] = 0; x[9] = 0; x[10] = 5; x[11] = 0 + + x[12] = 0; x[13] = 0; x[14] = 0; x[15] = 0 + x[16] = 0; x[17] = 5; x[18] = -3; x[19] = 0 + x[20] = 0; x[21] = -1; x[22] = 1; x[23] = 1 + + let maskPointer = UnsafeMutablePointer.allocate(capacity: 24) + let m = maskPointer + + m[0] = 1; m[1] = 1; m[2] = 1; m[3] = 0 + m[4] = 1; m[5] = 1; m[6] = 1; m[7] = 0 + m[8] = 1; m[9] = 1; m[10] = 1; m[11] = 0 + + m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 0 + m[16] = 0; m[17] = 1; m[18] = 1; m[19] = 1 + m[20] = 0; m[21] = 1; m[22] = 1; m[23] = 1 + + let preBN_mean: [Float] = [0] + let preBN_variance: [Float] = [0.9] + let preBN_epsilon: Float = 0.1 + let preBN_scale: [Float] = [1] + let preBN_bias: [Float] = [0] + + var preBN_mergedScale = SWBatchNormLayerDesc.mergeScales(scaleWeights: preBN_scale, + varianceWeights: preBN_variance, + epsilon: preBN_epsilon) + + var preBN_mergedBias = SWBatchNormLayerDesc.mergedBiases(biasWeights: preBN_bias, + meanWeights: preBN_mean, + mergedScales: preBN_mergedScale) + + let preBN = SWBatchNormLayerDesc(numChannels: trunkChannels, + mergedScale: &preBN_mergedScale, + mergedBias: &preBN_mergedBias) + + let regularConv = + SWConvLayerDesc(convYSize: 1, + convXSize: 1, + inChannels: trunkChannels, + outChannels: regularChannels, + dilationY: 1, + dilationX: 1, + weights: UnsafeMutablePointer.allocate(capacity: 1)) + + regularConv.weights[0] = 2 + + let convYSize: NSNumber = 3 + let convXSize: NSNumber = 3 + let capacity = convYSize.intValue * convXSize.intValue * gpoolChannels.intValue + + let gpoolConv = + SWConvLayerDesc(convYSize: convYSize, + convXSize: convXSize, + inChannels: trunkChannels, + outChannels: gpoolChannels, + dilationY: 1, + dilationX: 1, + weights: UnsafeMutablePointer.allocate(capacity: capacity)) + + let w = gpoolConv.weights; + + w[0] = 0; w[1] = 0; w[2] = 0 + w[3] = 0; w[4] = 0; w[5] = 1 + w[6] = 0; w[7] = 0; w[8] = 0 + + w[9] = 0; w[10] = 0; w[11] = 0 + w[12] = 1; w[13] = 0; w[14] = 0 + w[15] = 0; w[16] = 0; w[17] = 0 + + let gpoolBN_mean: [Float] = [0, 0] + let gpoolBN_variance: [Float] = [0.9, 0.9] + let gpoolBN_epsilon: Float = 0.1 + let gpoolBN_scale: [Float] = [1, 1] + let gpoolBN_bias: [Float] = [0, -2] + + var gpoolBN_mergedScale = SWBatchNormLayerDesc.mergeScales(scaleWeights: gpoolBN_scale, + varianceWeights: gpoolBN_variance, + epsilon: gpoolBN_epsilon) + + var gpoolBN_mergedBias = SWBatchNormLayerDesc.mergedBiases(biasWeights: gpoolBN_bias, + meanWeights: gpoolBN_mean, + mergedScales: gpoolBN_mergedScale) + + let gpoolBN = SWBatchNormLayerDesc(numChannels: gpoolChannels, + mergedScale: &gpoolBN_mergedScale, + mergedBias: &gpoolBN_mergedBias) + + let gpoolToBiasMul = + createSWMatMulLayerDesc(inChannels: 6, + outChannels: 1, + weights: UnsafeMutablePointer.allocate(capacity: 6)) + + gpoolToBiasMul.weights[0] = 36 + gpoolToBiasMul.weights[1] = 36 + gpoolToBiasMul.weights[2] = 18 + gpoolToBiasMul.weights[3] = 18 + gpoolToBiasMul.weights[4] = 1 + gpoolToBiasMul.weights[5] = 1 + + let midBN_mean: [Float] = [0] + let midBN_variance: [Float] = [0.9] + let midBN_epsilon: Float = 0.1 + let midBN_scale: [Float] = [1] + let midBN_bias: [Float] = [0] + + var midBN_mergedScale = SWBatchNormLayerDesc.mergeScales(scaleWeights: midBN_scale, + varianceWeights: midBN_variance, + epsilon: midBN_epsilon) + + var midBN_mergedBias = SWBatchNormLayerDesc.mergedBiases(biasWeights: midBN_bias, + meanWeights: midBN_mean, + mergedScales: midBN_mergedScale) + + let midBN = SWBatchNormLayerDesc(numChannels: 1, + mergedScale: &midBN_mergedScale, + mergedBias: &midBN_mergedBias) + + let finalConv = + SWConvLayerDesc(convYSize: 1, + convXSize: 1, + inChannels: 1, + outChannels: 1, + dilationY: 1, + dilationX: 1, + weights: UnsafeMutablePointer.allocate(capacity: 1)) + + finalConv.weights[0] = 1 + + let descriptor = SWGlobalPoolingResidualBlockDesc(preBN: preBN, + preActivation: ActivationKind.relu, + regularConv: regularConv, + gpoolConv: gpoolConv, + gpoolBN: gpoolBN, + gpoolActivation: ActivationKind.relu, + gpoolToBiasMul: gpoolToBiasMul, + midBN: midBN, + midActivation: ActivationKind.relu, + finalConv: finalConv) + + let outputPointer = UnsafeMutablePointer.allocate(capacity: 24) + + testGlobalPoolingResidualBlock(descriptor: descriptor, + batchSize: Int32(truncating: batchSize), + nnXLen: Int32(truncating: nnXLen), + nnYLen: Int32(truncating: nnYLen), + input: inputPointer, + mask: maskPointer, + output: outputPointer) + + let y = UnsafeMutablePointer.allocate(capacity: 24) + + y[0] = 3; y[1] = 6; y[2] = 0; y[3] = 0 + y[4] = 0; y[5] = 9; y[6] = 12; y[7] = 0 + y[8] = 0; y[9] = 0; y[10] = 15; y[11] = 0 + + y[12] = 0; y[13] = 0; y[14] = 0; y[15] = 0 + y[16] = 0; y[17] = 15; y[18] = -3; y[19] = 0 + y[20] = 0; y[21] = -1; y[22] = 3; y[23] = 3 + + for i in 0..<12 { + y[i] += 56 + (28 * (-11) * 0.1) + 5 + 4 + (2 * (-11) * 0.1) + 1 + y[i] *= m[i] + } + + for i in 12..<24 { + let sqrt6: Float32 = sqrt(6) + + y[i] += 12 + (6 * (sqrt6 - 14) * 0.1) + 1 + + 18 + (9 * (sqrt6 - 14) * 0.1) + 3 + + y[i] *= m[i] + } + + XCTAssertEqual(outputPointer[0], y[0], accuracy: 1e-4) + XCTAssertEqual(outputPointer[3], y[3], accuracy: 1e-4) + XCTAssertEqual(outputPointer[4], y[4], accuracy: 1e-4) + XCTAssertEqual(outputPointer[11], y[11], accuracy: 1e-4) + XCTAssertEqual(outputPointer[12], y[12], accuracy: 1e-4) + XCTAssertEqual(outputPointer[18], y[18], accuracy: 1e-4) + XCTAssertEqual(outputPointer[23], y[23], accuracy: 1e-4) + } +} + +final class NestedBottleneckResidualBlockTest: XCTestCase { + + func testFP32() { + let batchSize = 1 + let nnXLen = 1 + let nnYLen = 1 + let numChannels = 1 + + let graph = MPSGraph() + + let source = InputLayer(graph: graph, + nnXLen: nnXLen as NSNumber, + nnYLen: nnYLen as NSNumber, + numChannels: numChannels as NSNumber) + + let mask = MaskLayer(graph: graph, + nnXLen: nnXLen as NSNumber, + nnYLen: nnYLen as NSNumber) + + let maskSum = MaskSumLayer(graph: graph, + maskTensor: mask.tensor) + + let maskSumSqrtS14M01 = MaskSumSqrtS14M01Layer(graph: graph, + maskSum: maskSum) + + let preBN_mean: [Float] = [0] + let preBN_variance: [Float] = [0.9] + let preBN_epsilon: Float = 0.1 + let preBN_scale: [Float] = [1] + let preBN_bias: [Float] = [0] + + var preBN_mergedScale = SWBatchNormLayerDesc.mergeScales(scaleWeights: preBN_scale, + varianceWeights: preBN_variance, + epsilon: preBN_epsilon) + + var preBN_mergedBias = SWBatchNormLayerDesc.mergedBiases(biasWeights: preBN_bias, + meanWeights: preBN_mean, + mergedScales: preBN_mergedScale) + + let preBN = SWBatchNormLayerDesc(numChannels: numChannels as NSNumber, + mergedScale: &preBN_mergedScale, + mergedBias: &preBN_mergedBias) + + let preActivation = ActivationKind.mish + + let preConv = SWConvLayerDesc(convYSize: 1, + convXSize: 1, + inChannels: numChannels as NSNumber, + outChannels: numChannels as NSNumber, + dilationY: 1, + dilationX: 1, + weights: UnsafeMutablePointer.allocate(capacity: 1)) + + preConv.weights[0] = 1 + + let ordinary = SWResidualBlockDesc(preBN: preBN, + preActivation: preActivation, + regularConv: preConv, + midBN: preBN, + midActivation: preActivation, + finalConv: preConv) + + let nestedBottleneck = createSWNestedBottleneckResidualBlockDesc(preBN: preBN, + preActivation: preActivation, + preConv: preConv, + blockDescriptors: [ordinary], + postBN: preBN, + postActivation: preActivation, + postConv: preConv) + + let descriptor = SWNestedBottleneckResidualBlockDesc(preBN: preBN, + preActivation: preActivation, + preConv: preConv, + blockDescriptors: [nestedBottleneck], + postBN: preBN, + postActivation: preActivation, + postConv: preConv) + + let block = NestedBottleneckResidualBlock(graph: graph, + sourceTensor: source.tensor, + maskTensor: mask.tensor, + maskSumTensor: maskSum.tensor, + maskSumSqrtS14M01Tensor: maskSumSqrtS14M01.tensor, + descriptor: descriptor, + nnXLen: nnXLen as NSNumber, + nnYLen: nnYLen as NSNumber) + + let device = MTLCreateSystemDefaultDevice()! + + let inputArrayShape = InputShape.create(batchSize: batchSize as NSNumber, + numChannels: numChannels as NSNumber, + nnYLen: nnYLen as NSNumber, + nnXLen: nnXLen as NSNumber) + + let inLength = inputArrayShape.countElements() + let inputPointer = UnsafeMutablePointer.allocate(capacity: inLength) + inputPointer[0] = 1 + + let sourceDescriptor = MPSNDArrayDescriptor(dataType: source.tensor.dataType, + shape: inputArrayShape) + + let sourceArray = MPSNDArray(device: device, + descriptor: sourceDescriptor) + + sourceArray.writeBytes(inputPointer) + let sourceTensorData = MPSGraphTensorData(sourceArray) + + let maskArrayShape = InputShape.create(batchSize: batchSize as NSNumber, + numChannels: 1, + nnYLen: nnYLen as NSNumber, + nnXLen: nnXLen as NSNumber) + + let maskLength = maskArrayShape.countElements() + let maskPointer = UnsafeMutablePointer.allocate(capacity: maskLength) + maskPointer[0] = 1 + + let maskDescriptor = MPSNDArrayDescriptor(dataType: mask.tensor.dataType, + shape: maskArrayShape) + + let maskArray = MPSNDArray(device: device, + descriptor: maskDescriptor) + + maskArray.writeBytes(maskPointer) + let maskTensorData = MPSGraphTensorData(maskArray) + + let fetch = graph.run(feeds: [source.tensor: sourceTensorData, + mask.tensor: maskTensorData], + targetTensors: [block.resultTensor], + targetOperations: nil) + + let outputArray = fetch[block.resultTensor]?.mpsndarray() + let outLength = outputArray!.countElements() + let outputFP32 = UnsafeMutablePointer.allocate(capacity: outLength) + outputArray?.readBytes(outputFP32) + + XCTAssertEqual(outputFP32[0], 2.8582418, accuracy: 1e-4) + } +} + +final class MatMulLayerTest: XCTestCase { + + func testFP32() { + let batchSize = 2 + let nnXLen = 2 + let nnYLen = 1 + let inChannels = 2 + let outChannels = 3 + let weightsCount = inChannels * outChannels + let weights = UnsafeMutablePointer.allocate(capacity: weightsCount) + + for i in 0...allocate(capacity: inputCount) + + for i in 0...allocate(capacity: outputCount) + + fetch[matMulLayer.resultTensor]?.mpsndarray().readBytes(outputPointer) + + XCTAssertEqual(outputPointer[0], 3, accuracy: 1e-8) + XCTAssertEqual(outputPointer[1], 4, accuracy: 1e-8) + XCTAssertEqual(outputPointer[2], 5, accuracy: 1e-8) + XCTAssertEqual(outputPointer[3], 9, accuracy: 1e-8) + XCTAssertEqual(outputPointer[4], 14, accuracy: 1e-8) + XCTAssertEqual(outputPointer[5], 19, accuracy: 1e-8) + XCTAssertEqual(outputPointer[6], 15, accuracy: 1e-8) + XCTAssertEqual(outputPointer[7], 24, accuracy: 1e-8) + XCTAssertEqual(outputPointer[8], 33, accuracy: 1e-8) + XCTAssertEqual(outputPointer[9], 21, accuracy: 1e-8) + XCTAssertEqual(outputPointer[10], 34, accuracy: 1e-8) + XCTAssertEqual(outputPointer[11], 47, accuracy: 1e-8) + } + + func test2D() { + let batchSize = 2 + let inChannels = 3 + let outChannels = 4 + let weightsCount = inChannels * outChannels + let weights = UnsafeMutablePointer.allocate(capacity: weightsCount) + + for i in 0...allocate(capacity: inputCount) + + for i in 0...allocate(capacity: outputCount) + + fetch[matMulLayer.resultTensor]?.mpsndarray().readBytes(outputPointer) + + XCTAssertEqual(outputPointer[0], 20, accuracy: 1e-8) + XCTAssertEqual(outputPointer[1], 23, accuracy: 1e-8) + XCTAssertEqual(outputPointer[2], 26, accuracy: 1e-8) + XCTAssertEqual(outputPointer[3], 29, accuracy: 1e-8) + XCTAssertEqual(outputPointer[4], 56, accuracy: 1e-8) + XCTAssertEqual(outputPointer[5], 68, accuracy: 1e-8) + XCTAssertEqual(outputPointer[6], 80, accuracy: 1e-8) + XCTAssertEqual(outputPointer[7], 92, accuracy: 1e-8) + } + + func testUnity() { + let batchSize = 2 + let inChannels = 1 + let outChannels = 1 + let weightsCount = inChannels * outChannels + let weights = UnsafeMutablePointer.allocate(capacity: weightsCount) + + for i in 0...allocate(capacity: inputCount) + + for i in 0...allocate(capacity: outputCount) + + fetch[matMulLayer.resultTensor]?.mpsndarray().readBytes(outputPointer) + + XCTAssertEqual(outputPointer[0], 0, accuracy: 1e-8) + XCTAssertEqual(outputPointer[1], 1, accuracy: 1e-8) + } +} + +final class MatBiasLayerTest: XCTestCase { + + func testFP32() { + let numChannels = 2 + let weights = UnsafeMutablePointer.allocate(capacity: numChannels) + let shape = [8, 2] as [NSNumber] + + weights[0] = 1 + weights[1] = -1 + + let descriptor = createSWMatBiasLayerDesc(numChannels: Int32(numChannels), + weights: weights) + + let graph = MPSGraph() + + let inputTensor = graph.placeholder(shape: [8, 2], + dataType: MPSDataType.float32, + name: nil) + + let matBiasLayer = MatBiasLayer(graph: graph, + descriptor: descriptor, + sourceTensor: inputTensor) + + let inputPointer = UnsafeMutablePointer.allocate(capacity: 16) + + for i in 0..<16 { + inputPointer[i] = Float32(i) + } + + let device = MTLCreateSystemDefaultDevice()! + + let inputDescriptor = MPSNDArrayDescriptor(dataType: inputTensor.dataType, + shape: shape) + + let inputArray = MPSNDArray(device: device, + descriptor: inputDescriptor) + + inputArray.writeBytes(inputPointer) + let inputTensorData = MPSGraphTensorData(inputArray) + + let fetch = graph.run(feeds: [inputTensor: inputTensorData], + targetTensors: [matBiasLayer.resultTensor], + targetOperations: nil) + + let outputPointer = UnsafeMutablePointer.allocate(capacity: 16) + + fetch[matBiasLayer.resultTensor]?.mpsndarray().readBytes(outputPointer) + + XCTAssertEqual(outputPointer[0], 1, accuracy: 1e-8) + XCTAssertEqual(outputPointer[1], 0, accuracy: 1e-8) + XCTAssertEqual(outputPointer[2], 3, accuracy: 1e-8) + XCTAssertEqual(outputPointer[3], 2, accuracy: 1e-8) + XCTAssertEqual(outputPointer[15], 14, accuracy: 1e-8) + } + + func testUnity() { + let batchSize = 2 + let numChannels = 1 + let weightsCount = numChannels + let weights = UnsafeMutablePointer.allocate(capacity: weightsCount) + + for i in 0...allocate(capacity: inputCount) + + for i in 0...allocate(capacity: outputCount) + + fetch[matBiasLayer.resultTensor]?.mpsndarray().readBytes(outputPointer) + + XCTAssertEqual(outputPointer[0], 1, accuracy: 1e-8) + XCTAssertEqual(outputPointer[1], 2, accuracy: 1e-8) + } +} + +final class TrunkTest: XCTestCase { + + func testUnity() { + let batchSize = 2 + let nnXLen = 2 + let nnYLen = 2 + let numChannels = 2 + let unityConvWeights = UnsafeMutablePointer.allocate(capacity: numChannels * numChannels) + + unityConvWeights[0] = 1 + unityConvWeights[1] = 0 + unityConvWeights[2] = 0 + unityConvWeights[3] = 1 + + let unityConv = SWConvLayerDesc(convYSize: 1, + convXSize: 1, + inChannels: numChannels as NSNumber, + outChannels: numChannels as NSNumber, + dilationY: 1, + dilationX: 1, + weights: unityConvWeights) + + let initialMatMulWeights = + UnsafeMutablePointer.allocate(capacity: numChannels * numChannels) + + initialMatMulWeights[0] = 1 + initialMatMulWeights[1] = 0 + initialMatMulWeights[2] = 0 + initialMatMulWeights[3] = 1 + + let initialMatMul = SWMatMulLayerDesc(inChannels: numChannels as NSNumber, + outChannels: numChannels as NSNumber, + weights: initialMatMulWeights) + + let unityBN_mean: [Float] = [0, 0] + let unityBN_variance: [Float] = [0.9, 0.9] + let unityBN_epsilon: Float = 0.1 + let unityBN_scale: [Float] = [1, 1] + let unityBN_bias: [Float] = [0, 0] + + var unityBN_mergedScale = SWBatchNormLayerDesc.mergeScales(scaleWeights: unityBN_scale, + varianceWeights: unityBN_variance, + epsilon: unityBN_epsilon) + + var unityBN_mergedBias = SWBatchNormLayerDesc.mergedBiases(biasWeights: unityBN_bias, + meanWeights: unityBN_mean, + mergedScales: unityBN_mergedScale) + + let unityBN = SWBatchNormLayerDesc(numChannels: numChannels as NSNumber, + mergedScale: &unityBN_mergedScale, + mergedBias: &unityBN_mergedBias) + + let residualBlock = SWResidualBlockDesc(preBN: unityBN, + preActivation: ActivationKind.relu, + regularConv: unityConv, + midBN: unityBN, + midActivation: ActivationKind.relu, + finalConv: unityConv) + + let gpoolToBiasCount = 3 * numChannels * numChannels + let gpoolToBiasMulWeights = + UnsafeMutablePointer.allocate(capacity: 3 * numChannels * numChannels) + + for i in 0...allocate(capacity: inputCount) + + for i in 0...allocate(capacity: inputGlobalCount) + + for i in 0...allocate(capacity: maskCount) + + for i in 0...allocate(capacity: inputCount) + + fetch[trunk.resultTensor]?.mpsndarray().readBytes(outputPointer) + + XCTAssertEqual(outputPointer[0], 4, accuracy: 1e-8) + XCTAssertEqual(outputPointer[1], 8, accuracy: 1e-8) + XCTAssertEqual(outputPointer[2], 12, accuracy: 1e-8) + XCTAssertEqual(outputPointer[3], 16, accuracy: 1e-8) + XCTAssertEqual(outputPointer[15], 64, accuracy: 1e-8) + } +} + +final class PolicyHeadTest: XCTestCase { + + func testUnity() { + let batchSize = 2 + let nnXLen = 2 + let nnYLen = 2 + let inChannels = 2 + let outChannels = 1 + + let unityConvWeights = UnsafeMutablePointer.allocate(capacity: inChannels * inChannels) + + unityConvWeights[0] = 1 + unityConvWeights[1] = 0 + unityConvWeights[2] = 0 + unityConvWeights[3] = 1 + + let unityConv = SWConvLayerDesc(convYSize: 1, + convXSize: 1, + inChannels: inChannels as NSNumber, + outChannels: inChannels as NSNumber, + dilationY: 1, + dilationX: 1, + weights: unityConvWeights) + + let unityBN_mean: [Float] = [0, 0] + let unityBN_variance: [Float] = [0.9, 0.9] + let unityBN_epsilon: Float = 0.1 + let unityBN_scale: [Float] = [1, 1] + let unityBN_bias: [Float] = [0, 0] + + var unityBN_mergedScale = SWBatchNormLayerDesc.mergeScales(scaleWeights: unityBN_scale, + varianceWeights: unityBN_variance, + epsilon: unityBN_epsilon) + + var unityBN_mergedBias = SWBatchNormLayerDesc.mergedBiases(biasWeights: unityBN_bias, + meanWeights: unityBN_mean, + mergedScales: unityBN_mergedScale) + + let unityBN = SWBatchNormLayerDesc(numChannels: inChannels as NSNumber, + mergedScale: &unityBN_mergedScale, + mergedBias: &unityBN_mergedBias) + + let gpoolToBiasCount = 3 * inChannels * inChannels + let gpoolToBiasMulWeights = + UnsafeMutablePointer.allocate(capacity: 3 * inChannels * inChannels) + + for i in 0...allocate(capacity: inChannels * outChannels) + + p2ConvWeights[0] = 0.5 + p2ConvWeights[1] = 0.5 + + let p2Conv = SWConvLayerDesc(convYSize: 1, + convXSize: 1, + inChannels: inChannels as NSNumber, + outChannels: outChannels as NSNumber, + dilationY: 1, + dilationX: 1, + weights: p2ConvWeights) + + let gpoolToPassCount = 3 * inChannels * outChannels + let gpoolToPassMulWeights = + UnsafeMutablePointer.allocate(capacity: 3 * inChannels * outChannels) + + for i in 0...allocate(capacity: inputCount) + + for i in 0...allocate(capacity: maskCount) + + for i in 0...allocate(capacity: policyCount) + + fetch[policyHead.policyTensor]?.mpsndarray().readBytes(policyPointer) + + let policyPassCount = batchSize + + let policyPassPointer = UnsafeMutablePointer.allocate(capacity: policyPassCount) + + fetch[policyHead.policyPassTensor]?.mpsndarray().readBytes(policyPassPointer) + + XCTAssertEqual(policyPointer[0], 2, accuracy: 1e-8) + XCTAssertEqual(policyPointer[1], 3, accuracy: 1e-8) + XCTAssertEqual(policyPointer[2], 4, accuracy: 1e-8) + XCTAssertEqual(policyPointer[3], 5, accuracy: 1e-8) + XCTAssertEqual(policyPointer[4], 10, accuracy: 1e-8) + XCTAssertEqual(policyPointer[5], 11, accuracy: 1e-8) + XCTAssertEqual(policyPointer[6], 12, accuracy: 1e-8) + XCTAssertEqual(policyPointer[7], 13, accuracy: 1e-8) + XCTAssertEqual(policyPassPointer[0], 8.6, accuracy: 1e-4) + XCTAssertEqual(policyPassPointer[1], 21.4, accuracy: 1e-4) + } +} + +final class ComboLayerTest: XCTestCase { + + func testMatMulBiasLayer() { + let graph = MPSGraph() + let inputShape = [3, 2] as [NSNumber] + + let inputTensor = graph.placeholder(shape: inputShape, + dataType: .float32, + name: nil) + + let mulTensor = graph.constant(0, + shape: [2, 1], + dataType: .float32) + + let matMulTensor = graph.matrixMultiplication(primary: inputTensor, + secondary: mulTensor, + name: nil) + + let biasTensor = graph.constant(0, + shape: [1, 1], + dataType: .float32) + + let matBiasTensor = graph.addition(matMulTensor, + biasTensor, + name: nil) + + let device = MTLCreateSystemDefaultDevice()! + + let inputDescriptor = MPSNDArrayDescriptor(dataType: inputTensor.dataType, + shape: inputShape) + + let inputArray = MPSNDArray(device: device, + descriptor: inputDescriptor) + + let inputTensorData = MPSGraphTensorData(inputArray) + + graph.run(feeds: [inputTensor: inputTensorData], + targetTensors: [matBiasTensor], + targetOperations: nil) + + XCTAssert(matMulTensor.shape! == [3, 1]) + XCTAssert(matBiasTensor.shape! == [3, 1]) + } +} + +final class ValueHeadTest: XCTestCase { + + func testZero() { + let batchSize = 2 + let nnXLen = 2 + let nnYLen = 2 + let inChannels = 1 + let v1OutChannels = 2 + let v2OutChannels = 2 + let v3OutChannels = 1 + + let v1ConvCount = inChannels * v1OutChannels + let v1ConvWeights = UnsafeMutablePointer.allocate(capacity: v1ConvCount) + + for i in 0...allocate(capacity: v2MulCount) + + for i in 0...allocate(capacity: v2OutChannels) + + for i in 0...allocate(capacity: v3MulCount) + + for i in 0...allocate(capacity: v3OutChannels) + + for i in 0...allocate(capacity: vOwnershipConvCount) + + for i in 0...allocate(capacity: inputCount) + + for i in 0...allocate(capacity: maskCount) + + for i in 0...allocate(capacity: valueCount) + + fetch[valueHead.valueTensor]?.mpsndarray().readBytes(valuePointer) + + let scoreValueCount = batchSize * v3OutChannels + let scoreValuePointer = UnsafeMutablePointer.allocate(capacity: scoreValueCount) + + fetch[valueHead.scoreValueTensor]?.mpsndarray().readBytes(scoreValuePointer) + + let ownershipCount = batchSize * nnXLen * nnYLen * v3OutChannels + let ownershipPointer = UnsafeMutablePointer.allocate(capacity: ownershipCount) + + fetch[valueHead.ownershipTensor]?.mpsndarray().readBytes(ownershipPointer) + + XCTAssertEqual(valuePointer[0], 0, accuracy: 1e-8) + XCTAssertEqual(valuePointer[1], 0, accuracy: 1e-8) + XCTAssertEqual(scoreValuePointer[0], 0, accuracy: 1e-8) + XCTAssertEqual(scoreValuePointer[1], 0, accuracy: 1e-8) + XCTAssertEqual(ownershipPointer[0], 0, accuracy: 1e-8) + XCTAssertEqual(ownershipPointer[1], 0, accuracy: 1e-8) + XCTAssertEqual(ownershipPointer[2], 0, accuracy: 1e-8) + XCTAssertEqual(ownershipPointer[3], 0, accuracy: 1e-8) + XCTAssertEqual(ownershipPointer[4], 0, accuracy: 1e-8) + XCTAssertEqual(ownershipPointer[5], 0, accuracy: 1e-8) + XCTAssertEqual(ownershipPointer[6], 0, accuracy: 1e-8) + XCTAssertEqual(ownershipPointer[7], 0, accuracy: 1e-8) + } +} + +final class ComputeContextTest: XCTestCase { + + func testCreateInstance() { + let nnXLen: Int32 = 9 + let nnYLen: Int32 = 11 + + let context = createMetalComputeContext(nnXLen: nnXLen, + nnYLen: nnYLen) + + XCTAssert(context.nnXLen == nnXLen) + XCTAssert(context.nnYLen == nnYLen) + } +} + +final class ComputeHandleTest: XCTestCase { + let swModelDescTest = SWModelDescTest() + + func testCreateInstance() { + let context = createMetalComputeContext(nnXLen: 9, + nnYLen: 11) + + let swModelDesc = swModelDescTest.createMiniDesc() + + let handle = maybeCreateMetalComputeHandle(condition: true, + descriptor: swModelDesc, + context: context) + + XCTAssert(handle?.model.nnXLen == context.nnXLen as NSNumber) + XCTAssert(handle?.model.nnYLen == context.nnYLen as NSNumber) + XCTAssert(handle?.model.version == swModelDesc.version) + XCTAssert(handle?.model.numValueChannels == swModelDesc.numValueChannels) + XCTAssert(handle?.model.numScoreValueChannels == swModelDesc.numScoreValueChannels) + XCTAssert(handle?.model.numOwnershipChannels == swModelDesc.numOwnershipChannels) + } +} + +final class MetalBackendTest: XCTestCase { + let swModelDescTest = SWModelDescTest() + + func testPrintDevices() { + printMetalDevices() + } + + func testGetOutput() { + let context = createMetalComputeContext(nnXLen: 1, + nnYLen: 1) + + let swModelDesc = swModelDescTest.createMiniDesc() + + let handle = maybeCreateMetalComputeHandle(condition: true, + descriptor: swModelDesc, + context: context) + + var input = [Float32](repeating: 1, count: 1) + var inputGlobal = [Float32](repeating: 1, count: 1) + var inputMeta = [Float32](repeating: 0, count: 0) + var policyOutput = [Float32](repeating: 1, count: 1) + var policyPassOutput = [Float32](repeating: 1, count: 1) + var valueOutput = [Float32](repeating: 1, count: 1) + var scoreValueOutput = [Float32](repeating: 1, count: 1) + var ownershipOutput = [Float32](repeating: 1, count: 1) + + handle?.model.apply(input: &input, + inputGlobal: &inputGlobal, + inputMeta: &inputMeta, + policy: &policyOutput, + policyPass: &policyPassOutput, + value: &valueOutput, + scoreValue: &scoreValueOutput, + ownership: &ownershipOutput, + batchSize: 1) + + XCTAssertEqual(policyOutput[0], 101.68, accuracy: 1e-4) + XCTAssertEqual(policyPassOutput[0], 68.88, accuracy: 1e-4) + XCTAssertEqual(valueOutput[0], 126.936, accuracy: 1e-4) + XCTAssertEqual(scoreValueOutput[0], 126.936, accuracy: 1e-4) + XCTAssertEqual(ownershipOutput[0], 32.8, accuracy: 1e-4) + } +} diff --git a/cpp/xcode/KataGoSwiftTests/ModelTest.swift b/cpp/xcode/KataGoSwiftTests/ModelTest.swift new file mode 100644 index 000000000..70c795feb --- /dev/null +++ b/cpp/xcode/KataGoSwiftTests/ModelTest.swift @@ -0,0 +1,976 @@ +// +// ModelTest.swift +// KataGoSwiftTests +// +// Created by Chin-Chang Yang on 2024/7/19. +// + +import XCTest +import MetalPerformanceShadersGraph + +final class SWModelDescTest { + + var unityConvWeights = [Float](repeating: 1, count: 1) + var unityMatMulWeights = [Float](repeating: 1, count: 1) + var gpoolMatMulWeights = [Float](repeating: 3, count: 3) + var zeroMatBiasWeights = [Float](repeating: 0, count: 1) + var gpoolToPassMulWeights = [Float](repeating: 3, count: 9) + var gpoolToPassBiasWeights = [Float](repeating: 0, count: 3) + var mergedScale: [Float] = [1] + var mergedBias: [Float] = [0] + + func createMiniDescV15Meta() -> SWModelDesc { + let version = 15 + + let unityConv = SWConvLayerDesc(convYSize: 1, + convXSize: 1, + inChannels: 1, + outChannels: 1, + dilationY: 1, + dilationX: 1, + weights: &unityConvWeights) + + let unityMatMul = SWMatMulLayerDesc(inChannels: 1, + outChannels: 1, + weights: &unityMatMulWeights) + + + let unityBatchNorm = SWBatchNormLayerDesc(numChannels: 1, + mergedScale: &mergedScale, + mergedBias: &mergedBias) + + let unityResidual = SWResidualBlockDesc(preBN: unityBatchNorm, + preActivation: ActivationKind.relu, + regularConv: unityConv, + midBN: unityBatchNorm, + midActivation: ActivationKind.relu, + finalConv: unityConv) + + let gpoolMatMul = SWMatMulLayerDesc(inChannels: 3, + outChannels: 1, + weights: &gpoolMatMulWeights) + + let globalPooling = + SWGlobalPoolingResidualBlockDesc(preBN: unityBatchNorm, + preActivation: ActivationKind.relu, + regularConv: unityConv, + gpoolConv: unityConv, + gpoolBN: unityBatchNorm, + gpoolActivation: ActivationKind.relu, + gpoolToBiasMul: gpoolMatMul, + midBN: unityBatchNorm, + midActivation: ActivationKind.relu, + finalConv: unityConv) + + let blocks: [BlockDescriptor] = [unityResidual, + BlockDescriptor(), + globalPooling, + unityResidual] + + let zeroMatBias = SWMatBiasLayerDesc(numChannels: 1, + weights: &zeroMatBiasWeights) + + let sgfMetadataEncoder = SWSGFMetadataEncoderDesc(version: version, + numInputMetaChannels: 1, + mul1: unityMatMul, + bias1: zeroMatBias, + act1: ActivationKind.relu, + mul2: unityMatMul, + bias2: zeroMatBias, + act2: ActivationKind.relu, + mul3: unityMatMul) + + let trunkDesc = SWTrunkDesc(version: version, + trunkNumChannels: 1, + midNumChannels: 1, + regularNumChannels: 1, + gpoolNumChannels: 1, + initialConv: unityConv, + initialMatMul: unityMatMul, + sgfMetadataEncoder: sgfMetadataEncoder, + blockDescriptors: blocks, + trunkTipBN: unityBatchNorm, + trunkTipActivation: ActivationKind.relu) + + let gpoolToPassMul = SWMatMulLayerDesc(inChannels: 3, + outChannels: 3, + weights: &gpoolToPassMulWeights) + + let gpoolToPassBias = SWMatBiasLayerDesc(numChannels: 3, + weights: &gpoolToPassBiasWeights) + + let policyHead = createSWPolicyHeadDesc(version: Int32(version), + p1Conv: unityConv, + g1Conv: unityConv, + g1BN: unityBatchNorm, + g1Activation: ActivationKind.relu, + gpoolToBiasMul: gpoolMatMul, + p1BN: unityBatchNorm, + p1Activation: ActivationKind.relu, + p2Conv: unityConv, + gpoolToPassMul: gpoolToPassMul, + gpoolToPassBias: gpoolToPassBias, + passActivation: ActivationKind.relu, + gpoolToPassMul2: gpoolMatMul) + + let valueHead = SWValueHeadDesc(version: version, + v1Conv: unityConv, + v1BN: unityBatchNorm, + v1Activation: ActivationKind.relu, + v2Mul: gpoolMatMul, + v2Bias: zeroMatBias, + v2Activation: ActivationKind.relu, + v3Mul: unityMatMul, + v3Bias: zeroMatBias, + sv3Mul: unityMatMul, + sv3Bias: zeroMatBias, + vOwnershipConv: unityConv) + + let modelDesc = createSWModelDesc(version: Int32(version), + name: "test", + numInputChannels: 1, + numInputGlobalChannels: 1, + numInputMetaChannels: 1, + numValueChannels: 1, + numScoreValueChannels: 1, + numOwnershipChannels: 1, + trunk: trunkDesc, + policyHead: policyHead, + valueHead: valueHead) + + return modelDesc + } + + func createMiniDescV15() -> SWModelDesc { + let version = 15 + + let unityConv = SWConvLayerDesc(convYSize: 1, + convXSize: 1, + inChannels: 1, + outChannels: 1, + dilationY: 1, + dilationX: 1, + weights: &unityConvWeights) + + let unityMatMul = SWMatMulLayerDesc(inChannels: 1, + outChannels: 1, + weights: &unityMatMulWeights) + + + let unityBatchNorm = SWBatchNormLayerDesc(numChannels: 1, + mergedScale: &mergedScale, + mergedBias: &mergedBias) + + let unityResidual = SWResidualBlockDesc(preBN: unityBatchNorm, + preActivation: ActivationKind.relu, + regularConv: unityConv, + midBN: unityBatchNorm, + midActivation: ActivationKind.relu, + finalConv: unityConv) + + let gpoolMatMul = SWMatMulLayerDesc(inChannels: 3, + outChannels: 1, + weights: &gpoolMatMulWeights) + + let globalPooling = + SWGlobalPoolingResidualBlockDesc(preBN: unityBatchNorm, + preActivation: ActivationKind.relu, + regularConv: unityConv, + gpoolConv: unityConv, + gpoolBN: unityBatchNorm, + gpoolActivation: ActivationKind.relu, + gpoolToBiasMul: gpoolMatMul, + midBN: unityBatchNorm, + midActivation: ActivationKind.relu, + finalConv: unityConv) + + let blocks: [BlockDescriptor] = [unityResidual, + BlockDescriptor(), + globalPooling, + unityResidual] + + let trunkDesc = SWTrunkDesc(version: version, + trunkNumChannels: 1, + midNumChannels: 1, + regularNumChannels: 1, + gpoolNumChannels: 1, + initialConv: unityConv, + initialMatMul: unityMatMul, + sgfMetadataEncoder: nil, + blockDescriptors: blocks, + trunkTipBN: unityBatchNorm, + trunkTipActivation: ActivationKind.relu) + + let gpoolToPassMul = SWMatMulLayerDesc(inChannels: 3, + outChannels: 3, + weights: &gpoolToPassMulWeights) + + let gpoolToPassBias = SWMatBiasLayerDesc(numChannels: 3, + weights: &gpoolToPassBiasWeights) + + let policyHead = createSWPolicyHeadDesc(version: Int32(version), + p1Conv: unityConv, + g1Conv: unityConv, + g1BN: unityBatchNorm, + g1Activation: ActivationKind.relu, + gpoolToBiasMul: gpoolMatMul, + p1BN: unityBatchNorm, + p1Activation: ActivationKind.relu, + p2Conv: unityConv, + gpoolToPassMul: gpoolToPassMul, + gpoolToPassBias: gpoolToPassBias, + passActivation: ActivationKind.relu, + gpoolToPassMul2: gpoolMatMul) + + let zeroMatBias = SWMatBiasLayerDesc(numChannels: 1, + weights: &zeroMatBiasWeights) + + let valueHead = SWValueHeadDesc(version: version, + v1Conv: unityConv, + v1BN: unityBatchNorm, + v1Activation: ActivationKind.relu, + v2Mul: gpoolMatMul, + v2Bias: zeroMatBias, + v2Activation: ActivationKind.relu, + v3Mul: unityMatMul, + v3Bias: zeroMatBias, + sv3Mul: unityMatMul, + sv3Bias: zeroMatBias, + vOwnershipConv: unityConv) + + let modelDesc = createSWModelDesc(version: Int32(version), + name: "test", + numInputChannels: 1, + numInputGlobalChannels: 1, + numInputMetaChannels: 0, + numValueChannels: 1, + numScoreValueChannels: 1, + numOwnershipChannels: 1, + trunk: trunkDesc, + policyHead: policyHead, + valueHead: valueHead) + + return modelDesc + } + + func createMiniDesc() -> SWModelDesc { + let unityConv = SWConvLayerDesc(convYSize: 1, + convXSize: 1, + inChannels: 1, + outChannels: 1, + dilationY: 1, + dilationX: 1, + weights: &unityConvWeights) + + let unityMatMul = SWMatMulLayerDesc(inChannels: 1, + outChannels: 1, + weights: &unityMatMulWeights) + + + let unityBatchNorm = SWBatchNormLayerDesc(numChannels: 1, + mergedScale: &mergedScale, + mergedBias: &mergedBias) + + let unityResidual = SWResidualBlockDesc(preBN: unityBatchNorm, + preActivation: ActivationKind.relu, + regularConv: unityConv, + midBN: unityBatchNorm, + midActivation: ActivationKind.relu, + finalConv: unityConv) + + let gpoolMatMul = SWMatMulLayerDesc(inChannels: 3, + outChannels: 1, + weights: &gpoolMatMulWeights) + + let globalPooling = + SWGlobalPoolingResidualBlockDesc(preBN: unityBatchNorm, + preActivation: ActivationKind.relu, + regularConv: unityConv, + gpoolConv: unityConv, + gpoolBN: unityBatchNorm, + gpoolActivation: ActivationKind.relu, + gpoolToBiasMul: gpoolMatMul, + midBN: unityBatchNorm, + midActivation: ActivationKind.relu, + finalConv: unityConv) + + let blocks: [BlockDescriptor] = [unityResidual, + BlockDescriptor(), + globalPooling, + unityResidual] + + let trunkDesc = SWTrunkDesc(version: 0, + trunkNumChannels: 1, + midNumChannels: 1, + regularNumChannels: 1, + gpoolNumChannels: 1, + initialConv: unityConv, + initialMatMul: unityMatMul, + sgfMetadataEncoder: nil, + blockDescriptors: blocks, + trunkTipBN: unityBatchNorm, + trunkTipActivation: ActivationKind.relu) + + let gpoolToPassBias = SWMatBiasLayerDesc(numChannels: 3, + weights: &gpoolToPassBiasWeights) + + let policyHead = createSWPolicyHeadDesc(version: 0, + p1Conv: unityConv, + g1Conv: unityConv, + g1BN: unityBatchNorm, + g1Activation: ActivationKind.relu, + gpoolToBiasMul: gpoolMatMul, + p1BN: unityBatchNorm, + p1Activation: ActivationKind.relu, + p2Conv: unityConv, + gpoolToPassMul: gpoolMatMul, + gpoolToPassBias: gpoolToPassBias, + passActivation: ActivationKind.relu, + gpoolToPassMul2: gpoolMatMul) + + let zeroMatBias = SWMatBiasLayerDesc(numChannels: 1, + weights: &zeroMatBiasWeights) + + let valueHead = SWValueHeadDesc(version: 0, + v1Conv: unityConv, + v1BN: unityBatchNorm, + v1Activation: ActivationKind.relu, + v2Mul: gpoolMatMul, + v2Bias: zeroMatBias, + v2Activation: ActivationKind.relu, + v3Mul: unityMatMul, + v3Bias: zeroMatBias, + sv3Mul: unityMatMul, + sv3Bias: zeroMatBias, + vOwnershipConv: unityConv) + + let modelDesc = createSWModelDesc(version: 0, + name: "test", + numInputChannels: 1, + numInputGlobalChannels: 1, + numInputMetaChannels: 0, + numValueChannels: 1, + numScoreValueChannels: 1, + numOwnershipChannels: 1, + trunk: trunkDesc, + policyHead: policyHead, + valueHead: valueHead) + + return modelDesc + } +} + +final class ModelTest: XCTestCase { + let swModelDescTest = SWModelDescTest() + + func createMiniModelV15Meta() -> Model? { + let modelDesc = swModelDescTest.createMiniDescV15Meta() + + let device = MTLCreateSystemDefaultDevice()! + + let model = Model(device: device, + graph: MPSGraph(), + descriptor: modelDesc, + nnXLen: 1, + nnYLen: 1) + + return model + } + + func createMiniModelV15() -> Model? { + let modelDesc = swModelDescTest.createMiniDescV15() + + let device = MTLCreateSystemDefaultDevice()! + + let model = Model(device: device, + graph: MPSGraph(), + descriptor: modelDesc, + nnXLen: 1, + nnYLen: 1) + + return model + } + + func testMiniModelV15Meta() { + let model = createMiniModelV15Meta() + var input = [Float32](repeating: 1, count: 1) + var inputGlobal = [Float32](repeating: 1, count: 1) + var inputMeta = [Float32](repeating: 1, count: 1) + var policyOutput = [Float32](repeating: 1, count: 1) + var policyPassOutput = [Float32](repeating: 1, count: 1) + var valueOutput = [Float32](repeating: 1, count: 1) + var scoreValueOutput = [Float32](repeating: 1, count: 1) + var ownershipOutput = [Float32](repeating: 1, count: 1) + + model?.apply(input: &input, + inputGlobal: &inputGlobal, + inputMeta: &inputMeta, + policy: &policyOutput, + policyPass: &policyPassOutput, + value: &valueOutput, + scoreValue: &scoreValueOutput, + ownership: &ownershipOutput, + batchSize: 1) + + XCTAssertEqual(policyOutput[0], 152.51999, accuracy: 1e-4) + XCTAssertEqual(policyPassOutput[0], 929.87976, accuracy: 1e-4) + XCTAssertEqual(valueOutput[0], 190.40402, accuracy: 1e-4) + XCTAssertEqual(scoreValueOutput[0], 190.40402, accuracy: 1e-4) + XCTAssertEqual(ownershipOutput[0], 49.199997, accuracy: 1e-4) + } + + func testMiniModelV15() { + let model = createMiniModelV15() + var input = [Float32](repeating: 1, count: 1) + var inputGlobal = [Float32](repeating: 1, count: 1) + var inputMeta = [Float32](repeating: 0, count: 0) + var policyOutput = [Float32](repeating: 1, count: 1) + var policyPassOutput = [Float32](repeating: 1, count: 1) + var valueOutput = [Float32](repeating: 1, count: 1) + var scoreValueOutput = [Float32](repeating: 1, count: 1) + var ownershipOutput = [Float32](repeating: 1, count: 1) + + model?.apply(input: &input, + inputGlobal: &inputGlobal, + inputMeta: &inputMeta, + policy: &policyOutput, + policyPass: &policyPassOutput, + value: &valueOutput, + scoreValue: &scoreValueOutput, + ownership: &ownershipOutput, + batchSize: 1) + + XCTAssertEqual(policyOutput[0], 101.68, accuracy: 1e-4) + XCTAssertEqual(policyPassOutput[0], 619.9198, accuracy: 1e-4) + XCTAssertEqual(valueOutput[0], 126.936, accuracy: 1e-4) + XCTAssertEqual(scoreValueOutput[0], 126.936, accuracy: 1e-4) + XCTAssertEqual(ownershipOutput[0], 32.8, accuracy: 1e-4) + } + + func createMiniModel() -> Model? { + let modelDesc = swModelDescTest.createMiniDesc() + + let device = MTLCreateSystemDefaultDevice()! + + let model = Model(device: device, + graph: MPSGraph(), + descriptor: modelDesc, + nnXLen: 1, + nnYLen: 1) + + var input = [Float32](repeating: 1, count: 1) + var inputGlobal = [Float32](repeating: 1, count: 1) + var inputMeta = [Float32](repeating: 0, count: 0) + var policyOutput = [Float32](repeating: 1, count: 1) + var policyPassOutput = [Float32](repeating: 1, count: 1) + var valueOutput = [Float32](repeating: 1, count: 1) + var scoreValueOutput = [Float32](repeating: 1, count: 1) + var ownershipOutput = [Float32](repeating: 1, count: 1) + + model.apply(input: &input, + inputGlobal: &inputGlobal, + inputMeta: &inputMeta, + policy: &policyOutput, + policyPass: &policyPassOutput, + value: &valueOutput, + scoreValue: &scoreValueOutput, + ownership: &ownershipOutput, + batchSize: 1) + + return model + } + + func testMiniModel() { + let model = createMiniModel() + var input = [Float32](repeating: 1, count: 1) + var inputGlobal = [Float32](repeating: 1, count: 1) + var inputMeta = [Float32](repeating: 0, count: 0) + var policyOutput = [Float32](repeating: 1, count: 1) + var policyPassOutput = [Float32](repeating: 1, count: 1) + var valueOutput = [Float32](repeating: 1, count: 1) + var scoreValueOutput = [Float32](repeating: 1, count: 1) + var ownershipOutput = [Float32](repeating: 1, count: 1) + + model?.apply(input: &input, + inputGlobal: &inputGlobal, + inputMeta: &inputMeta, + policy: &policyOutput, + policyPass: &policyPassOutput, + value: &valueOutput, + scoreValue: &scoreValueOutput, + ownership: &ownershipOutput, + batchSize: 1) + + XCTAssertEqual(policyOutput[0], 101.68, accuracy: 1e-4) + XCTAssertEqual(policyPassOutput[0], 68.88, accuracy: 1e-4) + XCTAssertEqual(valueOutput[0], 126.936, accuracy: 1e-4) + XCTAssertEqual(scoreValueOutput[0], 126.936, accuracy: 1e-4) + XCTAssertEqual(ownershipOutput[0], 32.8, accuracy: 1e-4) + } + + func testMiniModelNHWC() { + let model = createMiniModel() + var input = [Float32](repeating: 1, count: 1) + var inputGlobal = [Float32](repeating: 1, count: 1) + var inputMeta = [Float32](repeating: 0, count: 0) + var policyOutput = [Float32](repeating: 1, count: 1) + var policyPassOutput = [Float32](repeating: 1, count: 1) + var valueOutput = [Float32](repeating: 1, count: 1) + var scoreValueOutput = [Float32](repeating: 1, count: 1) + var ownershipOutput = [Float32](repeating: 1, count: 1) + + model?.apply(input: &input, + inputGlobal: &inputGlobal, + inputMeta: &inputMeta, + policy: &policyOutput, + policyPass: &policyPassOutput, + value: &valueOutput, + scoreValue: &scoreValueOutput, + ownership: &ownershipOutput, + batchSize: 1) + + XCTAssertEqual(policyOutput[0], 101.68, accuracy: 1e-4) + XCTAssertEqual(policyPassOutput[0], 68.88, accuracy: 1e-4) + XCTAssertEqual(valueOutput[0], 126.936, accuracy: 1e-4) + XCTAssertEqual(scoreValueOutput[0], 126.936, accuracy: 1e-4) + XCTAssertEqual(ownershipOutput[0], 32.8, accuracy: 1e-4) + } + + func createBuffers(batchSize: Int, + nnYLen: Int, + nnXLen: Int, + numInputChannels: Int, + numInputGlobalChannels: Int, + numValueChannels: Int, + numScoreValueChannels: Int, + numOwnershipChannels: Int) -> (UnsafeMutablePointer, + UnsafeMutablePointer, + UnsafeMutablePointer, + UnsafeMutablePointer, + UnsafeMutablePointer, + UnsafeMutablePointer, + UnsafeMutablePointer, + UnsafeMutablePointer) { + + let inputCount = batchSize * nnYLen * nnXLen * numInputChannels + let inputGlobalCount = batchSize * numInputGlobalChannels + let inputMeta = 0 + let policyCount = batchSize * nnYLen * nnXLen + let policyPassCount = batchSize + let valueCount = batchSize * numValueChannels + let scoreValueCount = batchSize * numScoreValueChannels + let ownershipCount = batchSize * nnYLen * nnXLen * numOwnershipChannels + + return (UnsafeMutablePointer.allocate(capacity: inputCount), + UnsafeMutablePointer.allocate(capacity: inputGlobalCount), + UnsafeMutablePointer.allocate(capacity: inputMeta), + UnsafeMutablePointer.allocate(capacity: policyCount), + UnsafeMutablePointer.allocate(capacity: policyPassCount), + UnsafeMutablePointer.allocate(capacity: valueCount), + UnsafeMutablePointer.allocate(capacity: scoreValueCount), + UnsafeMutablePointer.allocate(capacity: ownershipCount)) + } + + func createModelB40C256(batchSize: Int, + nnYLen: Int, + nnXLen: Int, + numInputChannels: Int, + numInputGlobalChannels: Int, + numValueChannels: Int, + numScoreValueChannels: Int, + numOwnershipChannels: Int) -> Model { + let version = 10 + let convCount = 3 * 3 * 256 * 256 + let normCount = 256 + let randomWeights = UnsafeMutablePointer.allocate(capacity: convCount) + let oneWeights = UnsafeMutablePointer.allocate(capacity: normCount) + + for i in 0.. +#import "../neuralnet/nninterface.h" +#import "../main.h" + +@interface TestNN : XCTestCase + +@end + +@implementation TestNN + +// Known issue: Merged scales and biases are missing in the batch norm layer tests +#if 0 +- (void)testNNLayer { + std::vector args; + MainCmds::runnnlayertests(args); +} +#endif + +- (void)testOwnership { + std::vector args; + args.push_back("katago"); + args.push_back("gtp.cfg"); + args.push_back("model.bin.gz"); + // Create new CoreML files + MainCmds::runownershiptests(args); + // Reuse the CoreML files + MainCmds::runownershiptests(args); +} + +- (void)testOwnershipV8 { + std::vector args; + args.push_back("katago"); + args.push_back("metal_gtp.cfg"); + args.push_back("modelv8.bin.gz"); + MainCmds::runownershiptests(args); +} + +- (void)testPrintDevices { + NeuralNet::printDevices(); +} + +@end diff --git a/cpp/xcode/setup.sh b/cpp/xcode/setup.sh new file mode 100755 index 000000000..cd0803145 --- /dev/null +++ b/cpp/xcode/setup.sh @@ -0,0 +1,21 @@ +#!/bin/sh +wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/kata1-b18c384nbt-s9996604416-d4316597426.bin.gz +mv kata1-b18c384nbt-s9996604416-d4316597426.bin.gz DerivedData/KataGo/Build/Products/Debug/model.bin.gz +wget https://github.com/lightvector/KataGo/releases/download/v1.4.5/g170-b40c256x2-s5095420928-d1229425124.bin.gz +mv g170-b40c256x2-s5095420928-d1229425124.bin.gz DerivedData/KataGo/Build/Products/Debug/modelv8.bin.gz +wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/KataGoModel19x19fp16v14s9996604416.mlpackage.zip +mv KataGoModel19x19fp16v14s9996604416.mlpackage.zip DerivedData/KataGo/Build/Products/Debug/ +rm -rf DerivedData/KataGo/Build/Products/Debug/KataGoModel19x19fp16.mlpackage +unzip DerivedData/KataGo/Build/Products/Debug/KataGoModel19x19fp16v14s9996604416.mlpackage.zip -d DerivedData/KataGo/Build/Products/Debug/ +mv DerivedData/KataGo/Build/Products/Debug/KataGoModel19x19fp16v14s9996604416.mlpackage DerivedData/KataGo/Build/Products/Debug/KataGoModel19x19fp16.mlpackage +wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/KataGoModel19x19fp32v15m1humanv0.mlpackage.zip +mv KataGoModel19x19fp32v15m1humanv0.mlpackage.zip DerivedData/KataGo/Build/Products/Debug/ +rm -rf DerivedData/KataGo/Build/Products/Debug/KataGoModel19x19fp32v15m1humanv0.mlpackage +unzip DerivedData/KataGo/Build/Products/Debug/KataGoModel19x19fp32v15m1humanv0.mlpackage.zip -d DerivedData/KataGo/Build/Products/Debug/ +mv DerivedData/KataGo/Build/Products/Debug/KataGoModel19x19fp32v15m1humanv0.mlpackage DerivedData/KataGo/Build/Products/Debug/KataGoModel19x19fp32m1.mlpackage +ln -s ../../../../../../configs/misc/coreml_example.cfg DerivedData/KataGo/Build/Products/Debug/gtp.cfg +ln -s ../../../../../../configs/misc/metal_gtp.cfg DerivedData/KataGo/Build/Products/Debug/metal_gtp.cfg +ln -s ../../../../../../tests DerivedData/KataGo/Build/Products/Debug/tests +ln -s ../Debug/model.bin.gz DerivedData/KataGo/Build/Products/Release/ +ln -s ../Debug/KataGoModel19x19fp16.mlpackage DerivedData/KataGo/Build/Products/Release/ +ln -s ../Debug/gtp.cfg DerivedData/KataGo/Build/Products/Release/ diff --git a/docs/CoreML_Backend.md b/docs/CoreML_Backend.md new file mode 100644 index 000000000..b3c589c15 --- /dev/null +++ b/docs/CoreML_Backend.md @@ -0,0 +1,196 @@ +# Documentation for Metal and CoreML Backends in KataGo +KataGo harnesses the advanced capabilities of Apple Silicon through the integration of the [Metal Performance Shaders Graph](https://developer.apple.com/documentation/metalperformanceshadersgraph) and [CoreML](https://developer.apple.com/documentation/coreml). This integration empowers KataGo with GPU acceleration and compatibility with the [Neural Engine](https://machinelearning.apple.com/research/neural-engine-transformers), ensuring exceptional performance levels. + +## Essential Software Installation +Before proceeding, ensure that the indispensable build tool, [Ninja](https://ninja-build.org) and [Xcode](https://developer.apple.com/xcode/) are installed. Execute the following command to install Ninja: +``` +brew install ninja +``` +This command installs [Ninja](https://ninja-build.org) onto your system. + +## Source Code Acquisition +For the creation of a KataGo executable and corresponding CoreML models, initiate by downloading the source code. Build KataGo equipped with the Metal and CoreML backends by executing: +``` +wget https://github.com/ChinChangYang/KataGo/archive/metal-coreml-stable.tar.gz +tar -zxvf metal-coreml-stable.tar.gz +``` +This command retrieves the `metal-coreml-stable` source code version and decompresses the tarball into the `KataGo-metal-coreml-stable` directory. + +## Preparing the Workspace +Transition into the workspace directory where the KataGo models and executable will be built: +``` +cd KataGo-metal-coreml-stable +``` + +## Compiling KataGo +Utilize [CMake](https://cmake.org) in conjunction with [Ninja](https://ninja-build.org) for compiling KataGo with the Metal and CoreML backends: +``` +cd cpp +mv CMakeLists.txt-macos CMakeLists.txt +mkdir -p build +cd build +cmake -G Ninja -DNO_GIT_REVISION=1 -DCMAKE_BUILD_TYPE=Release ../ +ninja +``` +Executing these commands compiles KataGo in the `cpp/build` directory. + +## Download the KataGo model +Acquire the KataGo model in binary format suitable for the Metal backend: +``` +wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/kata1-b18c384nbt-s9996604416-d4316597426.bin.gz +wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/KataGoModel19x19fp16v14s9996604416.mlpackage.zip +unzip KataGoModel19x19fp16v14s9996604416.mlpackage.zip +``` + +## Organizing Binary and CoreML Model +Optionally, relocate the binary model to the run directory. However, it is essential to link the CoreML model in the run directory to ensure its accessibility by the CoreML backend: +``` +ln -s KataGoModel19x19fp16v14s9996604416.mlpackage KataGoModel19x19fp16.mlpackage +``` + +## Utilization of KataGo +KataGo can be operated in several modes, thanks to its extensive command options. Here are three primary use cases: + +**Benchmark** + +To conduct a benchmark, use the `benchmark` command, specify the binary model location, and apply the `coreml_example.cfg` configuration: +``` +./katago benchmark -model kata1-b18c384nbt-s9996604416-d4316597426.bin.gz -config ../configs/misc/coreml_example.cfg -t 16 -v 1600 +``` +This command activates the benchmark mode utilizing both Metal and CoreML backends. + +**GTP** + +For running the GTP protocol, utilize the `gtp` command, specify the binary model location, and use the `coreml_example.cfg` configuration: +``` +./katago gtp -model kata1-b18c384nbt-s9996604416-d4316597426.bin.gz -config ../configs/misc/coreml_example.cfg +``` +This enables the GTP protocol leveraging Metal and CoreML backends. + +**Analysis** + +Activate the analysis engine with the `analysis` command, specify the binary model location, and use the `coreml_analysis.cfg` configuration: +``` +./katago analysis -model kata1-b18c384nbt-s9996604416-d4316597426.bin.gz -config ../configs/misc/coreml_analysis.cfg +``` +This initiates the analysis mode, taking advantage of both Metal and CoreML backends. + +## Updating the CoreML model + +### Prerequisite Software Installation + +Before initiating the update process, it is crucial to install the required software. Start by installing `miniconda`, then create and activate a Python environment specifically for `coremltools`. Follow these commands: + +``` +brew install miniconda +conda create -n coremltools python=3.8 +conda activate coremltools +pip install coremltools torch +``` + +This sequence first installs `miniconda`. Subsequently, a dedicated environment named `coremltools` is created using Python version 3.8. Finally, within this environment, `coremltools` and `torch` are installed, setting the stage for the model update process. + +### Downloading the Checkpoint File + +The next step involves acquiring the latest and most robust network checkpoint from the KataGo Networks. Navigate to [KataGo Networks](https://katagotraining.org/networks/) and select the strongest confidently-rated network available. For instance, if `kata1-b18c384nbt-s8526915840-d3929217702` is the latest, download the corresponding `.zip` file, such as `kata1-b18c384nbt-s8526915840-d3929217702.zip`. Upon downloading, unzip the file to access the `model.ckpt` checkpoint file. + +### Converting the Checkpoint File + +**To Binary Model** + +Utilize the `export_model_pytorch.py` script to transform the checkpoint file into a binary model compatible with the Metal backend: + +``` +python python/export_model_pytorch.py -checkpoint model.ckpt -export-dir model -model-name model -filename-prefix model -use-swa +gzip model/model.bin +``` + +Executing this command sequence generates a compressed binary model file named `model.bin.gz`. + +**To CoreML Model** + +Similarly, for converting the checkpoint file into a CoreML model, the `convert_coreml_pytorch.py` script is employed: + +``` +python python/convert_coreml_pytorch.py -checkpoint model.ckpt -use-swa +``` + +This script outputs the CoreML model directory `KataGoModel19x19fp16.mlpackage`, specifically tailored for the CoreML backend. + +However, it's important to note a specific scenario: If KataGo has been compiled with the option `COMPILE_MAX_BOARD_LEN=29` to support larger 29x29 board sizes, the CoreML model conversion requires an additional parameter. In such cases, include the `-pos-len 29` option in the script command to ensure compatibility with the larger board size. The command modifies as follows: + +``` +python python/convert_coreml_pytorch.py -checkpoint model.ckpt -use-swa -pos-len 29 +``` + +This adjustment in the command results in the creation of a distinct CoreML model directory, `KataGoModel29x29fp16.mlpackage`, specifically tailored for KataGo versions supporting board sizes up to 29x29. + +### Reorganizing the Models + +Post-conversion, it is advisable to reorganize the models for optimal accessibility. While relocating the binary model to the run directory is optional, linking the CoreML model within this directory is essential for its effective utilization by the CoreML backend. + +# Human-trained Model + +KataGo's human-trained model was first introduced in the [KataGo v1.15.0 release](https://github.com/lightvector/KataGo/releases/tag/v1.15.0). To run this advanced model with the Metal and CoreML backends, follow these steps: + +## Download the Models + +- Download the human-trained binary model: + +``` +wget https://github.com/lightvector/KataGo/releases/download/v1.15.0/b18c384nbt-humanv0.bin.gz +``` + +- Download the human-trained CoreML model: + +``` +wget https://github.com/ChinChangYang/KataGo/releases/download/v1.15.1-coreml2/KataGoModel19x19fp16v15m1humanv0.mlpackage.zip +unzip KataGoModel19x19fp16v15m1humanv0.mlpackage.zip +``` + +It is essential to link the human-trained CoreML model in the run directory to ensure its accessibility by the CoreML backend: + +``` +ln -s KataGoModel19x19fp16v15m1humanv0.mlpackage KataGoModel19x19fp16m1.mlpackage +``` + +Place the models in the run directory where the katago executable is built. + +## Configuring Multi-Threaded Metal and CoreML Execution + +To utilize the processing power of Metal and CoreML execution, you'll need to modify the gtp_human5k_coreml.cfg configuration file. Specifically, append the following lines at the end of the file: + +``` +# CoreML settings-------------------------------------- +numNNServerThreadsPerModel = 2 +coremlDeviceToUseThread0 = 0 # GPU +coremlDeviceToUseThread1 = 100 # Neural Engine +``` + +These configuration settings instruct the KataGo to utilize two threads for executing neural networks, leveraging both the GPU and Neural Engine resources. + +## Running the Human-trained CoreML Model + +- Run the following command: + +``` +./katago gtp -model kata1-b18c384nbt-s9996604416-d4316597426.bin.gz -human-model b18c384nbt-humanv0.bin.gz -config ../configs/misc/gtp_human5k_coreml.cfg +``` + +Note: Make sure that the human-trained CoreML model is in the same directory as the katago executable. + +## Updating the Human-trained CoreML Model + +- Download the checkpoint file + +``` +wget https://github.com/lightvector/KataGo/releases/download/v1.15.0/b18c384nbt-humanv0.ckpt +``` + +- Convert the checkpoint file to a CoreML model: + +``` +python python/convert_coreml_pytorch.py -checkpoint b18c384nbt-humanv0.ckpt -use-swa +``` + +This will output the CoreML model directory `KataGoModel19x19fp16m1.mlpackage`, tailored for the CoreML backend. diff --git a/ios/KataGo iOS/KataGo iOS.xcodeproj/project.pbxproj b/ios/KataGo iOS/KataGo iOS.xcodeproj/project.pbxproj new file mode 100644 index 000000000..1cd48852b --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS.xcodeproj/project.pbxproj @@ -0,0 +1,1955 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + E1183E662B081DAA00637D44 /* main.h in Headers */ = {isa = PBXBuildFile; fileRef = E118EF0C2B081D8500637D44 /* main.h */; }; + E118802E2B081E3900637D44 /* sgf.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836CA2B081DA700637D44 /* sgf.h */; }; + E118802F2B081E3900637D44 /* trainingwrite.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836CB2B081DA700637D44 /* trainingwrite.h */; }; + E11880302B081E3900637D44 /* homedata.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836CC2B081DA700637D44 /* homedata.h */; }; + E11880312B081E3900637D44 /* poswriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836CD2B081DA700637D44 /* poswriter.cpp */; }; + E11880322B081E3900637D44 /* loadmodel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836CE2B081DA700637D44 /* loadmodel.cpp */; }; + E11880332B081E3900637D44 /* trainingwrite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836CF2B081DA700637D44 /* trainingwrite.cpp */; }; + E11880342B081E3900637D44 /* homedata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836D02B081DA700637D44 /* homedata.cpp */; }; + E11880352B081E3900637D44 /* files.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836D12B081DA700637D44 /* files.cpp */; }; + E11880362B081E3900637D44 /* sgf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836D22B081DA700637D44 /* sgf.cpp */; }; + E11880372B081E3900637D44 /* numpywrite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836D32B081DA700637D44 /* numpywrite.cpp */; }; + E11880382B081E3900637D44 /* loadmodel.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836D42B081DA700637D44 /* loadmodel.h */; }; + E11880392B081E3900637D44 /* poswriter.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836D52B081DA700637D44 /* poswriter.h */; }; + E118803A2B081E3900637D44 /* files.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836D62B081DA700637D44 /* files.h */; }; + E118803B2B081E3900637D44 /* numpywrite.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836D72B081DA700637D44 /* numpywrite.h */; }; + E118803C2B081E3900637D44 /* using.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836D92B081DA700637D44 /* using.h */; }; + E118803D2B081E3900637D44 /* md5.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836DA2B081DA700637D44 /* md5.cpp */; }; + E118803E2B081E3900637D44 /* multithread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836DB2B081DA700637D44 /* multithread.cpp */; }; + E118803F2B081E3900637D44 /* fileutils.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836DC2B081DA700637D44 /* fileutils.h */; }; + E11880402B081E3900637D44 /* config_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836DD2B081DA700637D44 /* config_parser.cpp */; }; + E11880412B081E3900637D44 /* threadtest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836DE2B081DA700637D44 /* threadtest.cpp */; }; + E11880422B081E3900637D44 /* makedir.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836DF2B081DA700637D44 /* makedir.h */; }; + E11880432B081E3900637D44 /* base64.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836E02B081DA700637D44 /* base64.h */; }; + E11880442B081E3900637D44 /* config_parser.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836E12B081DA700637D44 /* config_parser.h */; }; + E11880452B081E3900637D44 /* threadsafecounter.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836E22B081DA700637D44 /* threadsafecounter.h */; }; + E11880462B081E3900637D44 /* base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836E32B081DA700637D44 /* base64.cpp */; }; + E11880472B081E3900637D44 /* elo.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836E42B081DA700637D44 /* elo.h */; }; + E11880482B081E3900637D44 /* mainargs.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836E52B081DA700637D44 /* mainargs.h */; }; + E11880492B081E3900637D44 /* global.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836E62B081DA700637D44 /* global.h */; }; + E118804A2B081E3900637D44 /* threadtest.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836E72B081DA700637D44 /* threadtest.h */; }; + E118804B2B081E3900637D44 /* os.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836E82B081DA700637D44 /* os.h */; }; + E118804C2B081E3900637D44 /* bsearch.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836E92B081DA700637D44 /* bsearch.h */; }; + E118804D2B081E3900637D44 /* md5.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836EA2B081DA700637D44 /* md5.h */; }; + E118804E2B081E3900637D44 /* fileutils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836EB2B081DA700637D44 /* fileutils.cpp */; }; + E118804F2B081E3900637D44 /* test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836EC2B081DA700637D44 /* test.cpp */; }; + E11880502B081E3900637D44 /* timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836ED2B081DA700637D44 /* timer.cpp */; }; + E11880512B081E3900637D44 /* test.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836EE2B081DA700637D44 /* test.h */; }; + E11880522B081E3900637D44 /* datetime.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836EF2B081DA700637D44 /* datetime.h */; }; + E11880532B081E3900637D44 /* mainargs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836F02B081DA700637D44 /* mainargs.cpp */; }; + E11880542B081E3900637D44 /* multithread.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836F12B081DA700637D44 /* multithread.h */; }; + E11880552B081E3900637D44 /* sha2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836F22B081DA700637D44 /* sha2.cpp */; }; + E11880562B081E3900637D44 /* commontypes.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836F32B081DA700637D44 /* commontypes.h */; }; + E11880572B081E3900637D44 /* simpleallocator.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836F42B081DA700637D44 /* simpleallocator.h */; }; + E11880582B081E3900637D44 /* timer.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836F52B081DA700637D44 /* timer.h */; }; + E11880592B081E3900637D44 /* sha2.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836F62B081DA700637D44 /* sha2.h */; }; + E118805A2B081E3900637D44 /* bsearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836F72B081DA700637D44 /* bsearch.cpp */; }; + E118805B2B081E3900637D44 /* rand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836F82B081DA700637D44 /* rand.cpp */; }; + E118805C2B081E3900637D44 /* prioritymutex.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836F92B081DA700637D44 /* prioritymutex.h */; }; + E118805D2B081E3900637D44 /* makedir.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836FA2B081DA700637D44 /* makedir.cpp */; }; + E118805E2B081E3900637D44 /* elo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836FB2B081DA700637D44 /* elo.cpp */; }; + E118805F2B081E3900637D44 /* rand.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836FC2B081DA700637D44 /* rand.h */; }; + E11880602B081E3900637D44 /* threadsafequeue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836FD2B081DA700637D44 /* threadsafequeue.cpp */; }; + E11880612B081E3900637D44 /* commandloop.h in Headers */ = {isa = PBXBuildFile; fileRef = E11836FE2B081DA700637D44 /* commandloop.h */; }; + E11880622B081E3900637D44 /* logger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11836FF2B081DA700637D44 /* logger.cpp */; }; + E11880632B081E3900637D44 /* rand_helpers.h in Headers */ = {isa = PBXBuildFile; fileRef = E11837002B081DA700637D44 /* rand_helpers.h */; }; + E11880642B081E3900637D44 /* rand_helpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837012B081DA700637D44 /* rand_helpers.cpp */; }; + E11880652B081E3900637D44 /* hash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837022B081DA700637D44 /* hash.cpp */; }; + E11880662B081E3900637D44 /* threadsafecounter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837032B081DA700637D44 /* threadsafecounter.cpp */; }; + E11880672B081E3900637D44 /* datetime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837042B081DA700637D44 /* datetime.cpp */; }; + E11880682B081E3900637D44 /* global.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837052B081DA700637D44 /* global.cpp */; }; + E11880692B081E3900637D44 /* logger.h in Headers */ = {isa = PBXBuildFile; fileRef = E11837062B081DA700637D44 /* logger.h */; }; + E118806A2B081E3900637D44 /* commandloop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837072B081DA700637D44 /* commandloop.cpp */; }; + E118806B2B081E3900637D44 /* threadsafequeue.h in Headers */ = {isa = PBXBuildFile; fileRef = E11837082B081DA700637D44 /* threadsafequeue.h */; }; + E118806C2B081E3900637D44 /* hash.h in Headers */ = {isa = PBXBuildFile; fileRef = E11837092B081DA700637D44 /* hash.h */; }; + E118806D2B081E3900637D44 /* throttle.h in Headers */ = {isa = PBXBuildFile; fileRef = E118370A2B081DA700637D44 /* throttle.h */; }; + E118806E2B081E3900637D44 /* fancymath.h in Headers */ = {isa = PBXBuildFile; fileRef = E118370B2B081DA700637D44 /* fancymath.h */; }; + E118806F2B081E3900637D44 /* fancymath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118370C2B081DA700637D44 /* fancymath.cpp */; }; + E11880762B081E3A00637D44 /* testsearchcommon.h in Headers */ = {isa = PBXBuildFile; fileRef = E11837152B081DA700637D44 /* testsearchcommon.h */; }; + E11880772B081E3A00637D44 /* testbook.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837162B081DA700637D44 /* testbook.cpp */; }; + E11880782B081E3A00637D44 /* testrules.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837172B081DA700637D44 /* testrules.cpp */; }; + E11880792B081E3A00637D44 /* testtime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837182B081DA700637D44 /* testtime.cpp */; }; + E118807A2B081E3A00637D44 /* testsgf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837192B081DA700637D44 /* testsgf.cpp */; }; + E118807F2B081E3A00637D44 /* testsearchv9.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118371F2B081DA700637D44 /* testsearchv9.cpp */; }; + E11880802B081E3A00637D44 /* tests.h in Headers */ = {isa = PBXBuildFile; fileRef = E11837202B081DA700637D44 /* tests.h */; }; + E11880812B081E3A00637D44 /* testsearchv8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837212B081DA700637D44 /* testsearchv8.cpp */; }; + E11880822B081E3A00637D44 /* testsearchnonn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837222B081DA700637D44 /* testsearchnonn.cpp */; }; + E11880832B081E3A00637D44 /* testsearchcommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837232B081DA700637D44 /* testsearchcommon.cpp */; }; + E11880842B081E3A00637D44 /* tinymodel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837242B081DA700637D44 /* tinymodel.cpp */; }; + E11880852B081E3A00637D44 /* testcommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837252B081DA700637D44 /* testcommon.cpp */; }; + E11880982B081E3A00637D44 /* testsymmetries.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118373F2B081DA700637D44 /* testsymmetries.cpp */; }; + E11880992B081E3A00637D44 /* tinymodeldata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837402B081DA700637D44 /* tinymodeldata.cpp */; }; + E11881222B081E3D00637D44 /* testownership.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837D02B081DA700637D44 /* testownership.cpp */; }; + E11881232B081E3D00637D44 /* testnninputs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837D12B081DA700637D44 /* testnninputs.cpp */; }; + E11881242B081E3D00637D44 /* testsearchmisc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837D22B081DA700637D44 /* testsearchmisc.cpp */; }; + E11881252B081E3D00637D44 /* testtrainingwrite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837D32B081DA700637D44 /* testtrainingwrite.cpp */; }; + E11881262B081E3D00637D44 /* testscore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837D42B081DA700637D44 /* testscore.cpp */; }; + E11881272B081E3D00637D44 /* testboardarea.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837D52B081DA700637D44 /* testboardarea.cpp */; }; + E11881282B081E3D00637D44 /* testnn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837D62B081DA700637D44 /* testnn.cpp */; }; + E11881342B081E3D00637D44 /* testconfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837E32B081DA700637D44 /* testconfig.cpp */; }; + E11881352B081E3D00637D44 /* testsearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837E42B081DA700637D44 /* testsearch.cpp */; }; + E11881462B081E3D00637D44 /* testsearchv3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837F92B081DA700637D44 /* testsearchv3.cpp */; }; + E11881472B081E3D00637D44 /* testmisc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837FA2B081DA700637D44 /* testmisc.cpp */; }; + E11881482B081E3D00637D44 /* testnnevalcanary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837FB2B081DA700637D44 /* testnnevalcanary.cpp */; }; + E11881492B081E3D00637D44 /* testboardbasic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11837FC2B081DA700637D44 /* testboardbasic.cpp */; }; + E118814A2B081E3D00637D44 /* tinymodel.h in Headers */ = {isa = PBXBuildFile; fileRef = E11837FD2B081DA700637D44 /* tinymodel.h */; }; + E118814B2B081E3D00637D44 /* desc.h in Headers */ = {isa = PBXBuildFile; fileRef = E11837FF2B081DA700637D44 /* desc.h */; }; + E118814C2B081E3D00637D44 /* coremlbackend.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838002B081DA700637D44 /* coremlbackend.cpp */; }; + E11881542B081E3E00637D44 /* desc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838092B081DA700637D44 /* desc.cpp */; }; + E118815B2B081E3E00637D44 /* coremlbackend.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838102B081DA700637D44 /* coremlbackend.h */; }; + E118815C2B081E3E00637D44 /* openclhelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838112B081DA700637D44 /* openclhelpers.cpp */; }; + E118815D2B081E3E00637D44 /* metalbackend.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838122B081DA700637D44 /* metalbackend.h */; }; + E118815F2B081E3E00637D44 /* nninterface.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838142B081DA700637D44 /* nninterface.h */; }; + E11881622B081E3E00637D44 /* modelversion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838172B081DA700637D44 /* modelversion.cpp */; }; + E11881632B081E3E00637D44 /* modelversion.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838182B081DA700637D44 /* modelversion.h */; }; + E11881642B081E3E00637D44 /* nninputs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838192B081DA700637D44 /* nninputs.cpp */; }; + E11881652B081E3E00637D44 /* activations.h in Headers */ = {isa = PBXBuildFile; fileRef = E118381A2B081DA700637D44 /* activations.h */; }; + E118816B2B081E3E00637D44 /* nninputs.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838202B081DA700637D44 /* nninputs.h */; }; + E118816F2B081E3E00637D44 /* nneval.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838242B081DA700637D44 /* nneval.h */; }; + E11881702B081E3E00637D44 /* metalbackend.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838252B081DA700637D44 /* metalbackend.cpp */; }; + E11881712B081E3E00637D44 /* nneval.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838262B081DA700637D44 /* nneval.cpp */; }; + E11881722B081E3E00637D44 /* graphhash.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838282B081DA700637D44 /* graphhash.h */; }; + E11881732B081E3E00637D44 /* board.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838292B081DA700637D44 /* board.cpp */; }; + E11881742B081E3E00637D44 /* boardhistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118382A2B081DA700637D44 /* boardhistory.cpp */; }; + E11881752B081E3E00637D44 /* rules.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118382B2B081DA700637D44 /* rules.cpp */; }; + E11881762B081E3E00637D44 /* board.h in Headers */ = {isa = PBXBuildFile; fileRef = E118382C2B081DA700637D44 /* board.h */; }; + E11881772B081E3E00637D44 /* graphhash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118382D2B081DA700637D44 /* graphhash.cpp */; }; + E11881782B081E3E00637D44 /* rules.h in Headers */ = {isa = PBXBuildFile; fileRef = E118382E2B081DA700637D44 /* rules.h */; }; + E11881792B081E3E00637D44 /* boardhistory.h in Headers */ = {isa = PBXBuildFile; fileRef = E118382F2B081DA700637D44 /* boardhistory.h */; }; + E118817A2B081E3E00637D44 /* analysisdata.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838312B081DA800637D44 /* analysisdata.h */; }; + E118817B2B081E3E00637D44 /* searchparams.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838322B081DA800637D44 /* searchparams.h */; }; + E118817C2B081E3E00637D44 /* timecontrols.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838332B081DA800637D44 /* timecontrols.cpp */; }; + E118817D2B081E3E00637D44 /* searchnodetable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838342B081DA800637D44 /* searchnodetable.cpp */; }; + E118817E2B081E3E00637D44 /* searchprint.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838352B081DA800637D44 /* searchprint.h */; }; + E118817F2B081E3E00637D44 /* patternbonustable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838362B081DA800637D44 /* patternbonustable.cpp */; }; + E11881802B081E3E00637D44 /* searchpuct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838372B081DA800637D44 /* searchpuct.cpp */; }; + E11881812B081E3E00637D44 /* subtreevaluebiastable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838382B081DA800637D44 /* subtreevaluebiastable.cpp */; }; + E11881822B081E3E00637D44 /* asyncbot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838392B081DA800637D44 /* asyncbot.cpp */; }; + E11881832B081E3E00637D44 /* searchprint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118383A2B081DA800637D44 /* searchprint.cpp */; }; + E11881842B081E3E00637D44 /* searchresults.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118383B2B081DA800637D44 /* searchresults.cpp */; }; + E11881852B081E3E00637D44 /* reportedsearchvalues.h in Headers */ = {isa = PBXBuildFile; fileRef = E118383C2B081DA800637D44 /* reportedsearchvalues.h */; }; + E11881862B081E3E00637D44 /* localpattern.h in Headers */ = {isa = PBXBuildFile; fileRef = E118383D2B081DA800637D44 /* localpattern.h */; }; + E11881872B081E3E00637D44 /* searchnode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118383E2B081DA800637D44 /* searchnode.cpp */; }; + E11881882B081E3E00637D44 /* mutexpool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118383F2B081DA800637D44 /* mutexpool.cpp */; }; + E11881892B081E3E00637D44 /* searchmirror.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838402B081DA800637D44 /* searchmirror.cpp */; }; + E118818A2B081E3E00637D44 /* reportedsearchvalues.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838412B081DA800637D44 /* reportedsearchvalues.cpp */; }; + E118818B2B081E3E00637D44 /* searchmultithreadhelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838422B081DA800637D44 /* searchmultithreadhelpers.cpp */; }; + E118818C2B081E3E00637D44 /* searchupdatehelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838432B081DA800637D44 /* searchupdatehelpers.cpp */; }; + E118818D2B081E3E00637D44 /* searchtimehelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838442B081DA800637D44 /* searchtimehelpers.cpp */; }; + E118818E2B081E3E00637D44 /* asyncbot.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838452B081DA800637D44 /* asyncbot.h */; }; + E118818F2B081E3E00637D44 /* localpattern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838462B081DA800637D44 /* localpattern.cpp */; }; + E11881902B081E3E00637D44 /* searchnodetable.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838472B081DA800637D44 /* searchnodetable.h */; }; + E11881912B081E3E00637D44 /* distributiontable.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838482B081DA800637D44 /* distributiontable.h */; }; + E11881922B081E3E00637D44 /* subtreevaluebiastable.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838492B081DA800637D44 /* subtreevaluebiastable.h */; }; + E11881932B081E3E00637D44 /* search.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118384A2B081DA800637D44 /* search.cpp */; }; + E11881942B081E3E00637D44 /* analysisdata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118384B2B081DA800637D44 /* analysisdata.cpp */; }; + E11881952B081E3E00637D44 /* patternbonustable.h in Headers */ = {isa = PBXBuildFile; fileRef = E118384C2B081DA800637D44 /* patternbonustable.h */; }; + E11881962B081E3E00637D44 /* searchhelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118384D2B081DA800637D44 /* searchhelpers.cpp */; }; + E11881972B081E3E00637D44 /* searchnnhelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118384E2B081DA800637D44 /* searchnnhelpers.cpp */; }; + E11881982B081E3E00637D44 /* mutexpool.h in Headers */ = {isa = PBXBuildFile; fileRef = E118384F2B081DA800637D44 /* mutexpool.h */; }; + E11881992B081E3E00637D44 /* searchparams.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838502B081DA800637D44 /* searchparams.cpp */; }; + E118819A2B081E3E00637D44 /* search.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838512B081DA800637D44 /* search.h */; }; + E118819B2B081E3E00637D44 /* timecontrols.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838522B081DA800637D44 /* timecontrols.h */; }; + E118819C2B081E3E00637D44 /* searchnode.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838532B081DA800637D44 /* searchnode.h */; }; + E118819D2B081E3E00637D44 /* distributiontable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838542B081DA800637D44 /* distributiontable.cpp */; }; + E118819E2B081E3E00637D44 /* searchexplorehelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838552B081DA800637D44 /* searchexplorehelpers.cpp */; }; + E11881BA2B081E3F00637D44 /* book.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838762B081DA800637D44 /* book.h */; }; + E11881BB2B081E3F00637D44 /* bookcssjs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838772B081DA800637D44 /* bookcssjs.cpp */; }; + E11881BC2B081E3F00637D44 /* book.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838782B081DA800637D44 /* book.cpp */; }; + E11881BD2B081E3F00637D44 /* play.h in Headers */ = {isa = PBXBuildFile; fileRef = E118387A2B081DA800637D44 /* play.h */; }; + E11881BE2B081E3F00637D44 /* setup.h in Headers */ = {isa = PBXBuildFile; fileRef = E118387B2B081DA800637D44 /* setup.h */; }; + E11881BF2B081E3F00637D44 /* play.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118387C2B081DA800637D44 /* play.cpp */; }; + E11881C02B081E3F00637D44 /* playsettings.h in Headers */ = {isa = PBXBuildFile; fileRef = E118387D2B081DA800637D44 /* playsettings.h */; }; + E11881C12B081E3F00637D44 /* selfplaymanager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118387E2B081DA800637D44 /* selfplaymanager.cpp */; }; + E11881C22B081E3F00637D44 /* gtpconfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118387F2B081DA800637D44 /* gtpconfig.cpp */; }; + E11881C32B081E3F00637D44 /* setup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838802B081DA800637D44 /* setup.cpp */; }; + E11881C42B081E3F00637D44 /* playsettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838812B081DA800637D44 /* playsettings.cpp */; }; + E11881C52B081E3F00637D44 /* selfplaymanager.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838822B081DA800637D44 /* selfplaymanager.h */; }; + E11881C62B081E3F00637D44 /* gtpconfig.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838832B081DA800637D44 /* gtpconfig.h */; }; + E11881C72B081E3F00637D44 /* playutils.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838842B081DA800637D44 /* playutils.h */; }; + E11881C82B081E3F00637D44 /* playutils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838852B081DA800637D44 /* playutils.cpp */; }; + E11881C92B081E3F00637D44 /* gitinfotemplate.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838862B081DA800637D44 /* gitinfotemplate.h */; }; + E11881CB2B081E3F00637D44 /* genbook.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838892B081DA800637D44 /* genbook.cpp */; }; + E11881CC2B081E3F00637D44 /* analysis.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118388A2B081DA800637D44 /* analysis.cpp */; }; + E11881CD2B081E3F00637D44 /* gputest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118388B2B081DA800637D44 /* gputest.cpp */; }; + E11881CE2B081E3F00637D44 /* runtests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118388C2B081DA800637D44 /* runtests.cpp */; }; + E11881CF2B081E3F00637D44 /* selfplay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118388D2B081DA800637D44 /* selfplay.cpp */; }; + E11881D02B081E3F00637D44 /* misc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118388E2B081DA800637D44 /* misc.cpp */; }; + E11881D12B081E3F00637D44 /* sandbox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E118388F2B081DA800637D44 /* sandbox.cpp */; }; + E11881D22B081E3F00637D44 /* gtp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838902B081DA800637D44 /* gtp.cpp */; }; + E11881D32B081E3F00637D44 /* gatekeeper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838912B081DA800637D44 /* gatekeeper.cpp */; }; + E11881D42B081E3F00637D44 /* evalsgf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838922B081DA800637D44 /* evalsgf.cpp */; }; + E11881D52B081E3F00637D44 /* benchmark.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838932B081DA800637D44 /* benchmark.cpp */; }; + E11881D62B081E3F00637D44 /* match.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838942B081DA800637D44 /* match.cpp */; }; + E11881D72B081E3F00637D44 /* tune.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838952B081DA800637D44 /* tune.cpp */; }; + E11881D82B081E3F00637D44 /* commandline.h in Headers */ = {isa = PBXBuildFile; fileRef = E11838962B081DA800637D44 /* commandline.h */; }; + E11881D92B081E3F00637D44 /* contribute.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838972B081DA800637D44 /* contribute.cpp */; }; + E11881DA2B081E3F00637D44 /* commandline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E11838982B081DA800637D44 /* commandline.cpp */; }; + E11887632B081E4E00637D44 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1183E5F2B081DA900637D44 /* main.cpp */; }; + E11887E42B0830C900637D44 /* KataGoSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = E11887E32B0830C900637D44 /* KataGoSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E11887E72B0830C900637D44 /* KataGoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E11887E12B0830C900637D44 /* KataGoSwift.framework */; }; + E11887E82B0830C900637D44 /* KataGoSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E11887E12B0830C900637D44 /* KataGoSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + E11887EF2B08310800637D44 /* coremlmodel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11887EC2B08310800637D44 /* coremlmodel.swift */; }; + E11887F02B08310800637D44 /* coremlbackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11887ED2B08310800637D44 /* coremlbackend.swift */; }; + E11887F12B08310800637D44 /* metalbackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11887EE2B08310800637D44 /* metalbackend.swift */; }; + E11887F42B08312F00637D44 /* KataGoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E11887E12B0830C900637D44 /* KataGoSwift.framework */; }; + E11887F52B0831B100637D44 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E18F3F712A5149AB00D335E1 /* libz.tbd */; }; + E118EE962B081C3300637D44 /* katago.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E118EE902B081C3200637D44 /* katago.framework */; }; + E118EE972B081C3300637D44 /* katago.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E118EE902B081C3200637D44 /* katago.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + E149B7F42B350EA8002B7F61 /* parallel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E149B7F22B350EA8002B7F61 /* parallel.cpp */; }; + E149B7F52B350EA8002B7F61 /* parallel.h in Headers */ = {isa = PBXBuildFile; fileRef = E149B7F32B350EA8002B7F61 /* parallel.h */; }; + E18F3E112A51466A00D335E1 /* KataGo_iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E18F3E102A51466A00D335E1 /* KataGo_iOSApp.swift */; }; + E18F3E132A51466A00D335E1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E18F3E122A51466A00D335E1 /* ContentView.swift */; }; + E18F3E152A51466C00D335E1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E18F3E142A51466C00D335E1 /* Assets.xcassets */; }; + E18F3E182A51466C00D335E1 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E18F3E172A51466C00D335E1 /* Preview Assets.xcassets */; }; + E18F3E222A51466C00D335E1 /* KataGo_iOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E18F3E212A51466C00D335E1 /* KataGo_iOSTests.swift */; }; + E18F3E2C2A51466C00D335E1 /* KataGo_iOSUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E18F3E2B2A51466C00D335E1 /* KataGo_iOSUITests.swift */; }; + E18F3E2E2A51466C00D335E1 /* KataGo_iOSUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E18F3E2D2A51466C00D335E1 /* KataGo_iOSUITestsLaunchTests.swift */; }; + E18F3F722A5149B300D335E1 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E18F3F712A5149AB00D335E1 /* libz.tbd */; }; + E18F3F772A514B9700D335E1 /* default_model.bin.gz in Resources */ = {isa = PBXBuildFile; fileRef = E18F3F742A514B9700D335E1 /* default_model.bin.gz */; }; + E18F3F782A514B9700D335E1 /* default_gtp.cfg in Resources */ = {isa = PBXBuildFile; fileRef = E18F3F752A514B9700D335E1 /* default_gtp.cfg */; }; + E19D2E362AC8E5DB00C2A807 /* KataGoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19D2E352AC8E5DB00C2A807 /* KataGoModel.swift */; }; + E19D2E382AC97FA300C2A807 /* ToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19D2E372AC97FA300C2A807 /* ToolbarView.swift */; }; + E1A26B4B2B47693300BA922B /* KataGoModel29x29fp16.mlpackage in Resources */ = {isa = PBXBuildFile; fileRef = E1A26B492B47684400BA922B /* KataGoModel29x29fp16.mlpackage */; }; + E1B63BE42AABDF3500094965 /* BoardLineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B63BE32AABDF3500094965 /* BoardLineView.swift */; }; + E1B922752A5179A7006D3137 /* KataGoHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = E1B922742A5179A7006D3137 /* KataGoHelper.mm */; }; + E1C682712AA2A4E7001B4F44 /* GobanView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1C682702AA2A4E7001B4F44 /* GobanView.swift */; }; + E1C682732AA2B122001B4F44 /* WoodView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1C682722AA2B122001B4F44 /* WoodView.swift */; }; + E1C682752AA2CC31001B4F44 /* CommandView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1C682742AA2CC31001B4F44 /* CommandView.swift */; }; + E1D7D3AB2AA7547D00556DFB /* ButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1D7D3AA2AA7547D00556DFB /* ButtonView.swift */; }; + E1D7D3AD2AA897C000556DFB /* StoneView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1D7D3AC2AA897C000556DFB /* StoneView.swift */; }; + E1D7D3B32AAA1F5600556DFB /* AnalysisView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1D7D3B22AAA1F5600556DFB /* AnalysisView.swift */; }; + E1E1717E2AB9DAED004DCC3C /* ConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E1717D2AB9DAED004DCC3C /* ConfigView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + E11887E52B0830C900637D44 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = E18F3E052A51466A00D335E1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E11887E02B0830C900637D44; + remoteInfo = KataGoSwift; + }; + E11887F22B08312600637D44 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = E18F3E052A51466A00D335E1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E11887E02B0830C900637D44; + remoteInfo = KataGoSwift; + }; + E118EE942B081C3300637D44 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = E18F3E052A51466A00D335E1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E118EE8F2B081C3200637D44; + remoteInfo = katago; + }; + E18F3E1E2A51466C00D335E1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = E18F3E052A51466A00D335E1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E18F3E0C2A51466A00D335E1; + remoteInfo = "KataGo iOS"; + }; + E18F3E282A51466C00D335E1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = E18F3E052A51466A00D335E1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E18F3E0C2A51466A00D335E1; + remoteInfo = "KataGo iOS"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + E118EE842B0819E500637D44 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + E118EE972B081C3300637D44 /* katago.framework in Embed Frameworks */, + E11887E82B0830C900637D44 /* KataGoSwift.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + E11836CA2B081DA700637D44 /* sgf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sgf.h; sourceTree = ""; }; + E11836CB2B081DA700637D44 /* trainingwrite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = trainingwrite.h; sourceTree = ""; }; + E11836CC2B081DA700637D44 /* homedata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = homedata.h; sourceTree = ""; }; + E11836CD2B081DA700637D44 /* poswriter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = poswriter.cpp; sourceTree = ""; }; + E11836CE2B081DA700637D44 /* loadmodel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = loadmodel.cpp; sourceTree = ""; }; + E11836CF2B081DA700637D44 /* trainingwrite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = trainingwrite.cpp; sourceTree = ""; }; + E11836D02B081DA700637D44 /* homedata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = homedata.cpp; sourceTree = ""; }; + E11836D12B081DA700637D44 /* files.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = files.cpp; sourceTree = ""; }; + E11836D22B081DA700637D44 /* sgf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sgf.cpp; sourceTree = ""; }; + E11836D32B081DA700637D44 /* numpywrite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = numpywrite.cpp; sourceTree = ""; }; + E11836D42B081DA700637D44 /* loadmodel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = loadmodel.h; sourceTree = ""; }; + E11836D52B081DA700637D44 /* poswriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = poswriter.h; sourceTree = ""; }; + E11836D62B081DA700637D44 /* files.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = files.h; sourceTree = ""; }; + E11836D72B081DA700637D44 /* numpywrite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = numpywrite.h; sourceTree = ""; }; + E11836D92B081DA700637D44 /* using.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = using.h; sourceTree = ""; }; + E11836DA2B081DA700637D44 /* md5.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = md5.cpp; sourceTree = ""; }; + E11836DB2B081DA700637D44 /* multithread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = multithread.cpp; sourceTree = ""; }; + E11836DC2B081DA700637D44 /* fileutils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fileutils.h; sourceTree = ""; }; + E11836DD2B081DA700637D44 /* config_parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = config_parser.cpp; sourceTree = ""; }; + E11836DE2B081DA700637D44 /* threadtest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = threadtest.cpp; sourceTree = ""; }; + E11836DF2B081DA700637D44 /* makedir.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = makedir.h; sourceTree = ""; }; + E11836E02B081DA700637D44 /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = ""; }; + E11836E12B081DA700637D44 /* config_parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config_parser.h; sourceTree = ""; }; + E11836E22B081DA700637D44 /* threadsafecounter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = threadsafecounter.h; sourceTree = ""; }; + E11836E32B081DA700637D44 /* base64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base64.cpp; sourceTree = ""; }; + E11836E42B081DA700637D44 /* elo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = elo.h; sourceTree = ""; }; + E11836E52B081DA700637D44 /* mainargs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mainargs.h; sourceTree = ""; }; + E11836E62B081DA700637D44 /* global.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = global.h; sourceTree = ""; }; + E11836E72B081DA700637D44 /* threadtest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = threadtest.h; sourceTree = ""; }; + E11836E82B081DA700637D44 /* os.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = os.h; sourceTree = ""; }; + E11836E92B081DA700637D44 /* bsearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bsearch.h; sourceTree = ""; }; + E11836EA2B081DA700637D44 /* md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = md5.h; sourceTree = ""; }; + E11836EB2B081DA700637D44 /* fileutils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fileutils.cpp; sourceTree = ""; }; + E11836EC2B081DA700637D44 /* test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test.cpp; sourceTree = ""; }; + E11836ED2B081DA700637D44 /* timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = timer.cpp; sourceTree = ""; }; + E11836EE2B081DA700637D44 /* test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = test.h; sourceTree = ""; }; + E11836EF2B081DA700637D44 /* datetime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = datetime.h; sourceTree = ""; }; + E11836F02B081DA700637D44 /* mainargs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mainargs.cpp; sourceTree = ""; }; + E11836F12B081DA700637D44 /* multithread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = multithread.h; sourceTree = ""; }; + E11836F22B081DA700637D44 /* sha2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sha2.cpp; sourceTree = ""; }; + E11836F32B081DA700637D44 /* commontypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = commontypes.h; sourceTree = ""; }; + E11836F42B081DA700637D44 /* simpleallocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simpleallocator.h; sourceTree = ""; }; + E11836F52B081DA700637D44 /* timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = timer.h; sourceTree = ""; }; + E11836F62B081DA700637D44 /* sha2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sha2.h; sourceTree = ""; }; + E11836F72B081DA700637D44 /* bsearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bsearch.cpp; sourceTree = ""; }; + E11836F82B081DA700637D44 /* rand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rand.cpp; sourceTree = ""; }; + E11836F92B081DA700637D44 /* prioritymutex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = prioritymutex.h; sourceTree = ""; }; + E11836FA2B081DA700637D44 /* makedir.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = makedir.cpp; sourceTree = ""; }; + E11836FB2B081DA700637D44 /* elo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = elo.cpp; sourceTree = ""; }; + E11836FC2B081DA700637D44 /* rand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rand.h; sourceTree = ""; }; + E11836FD2B081DA700637D44 /* threadsafequeue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = threadsafequeue.cpp; sourceTree = ""; }; + E11836FE2B081DA700637D44 /* commandloop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = commandloop.h; sourceTree = ""; }; + E11836FF2B081DA700637D44 /* logger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = logger.cpp; sourceTree = ""; }; + E11837002B081DA700637D44 /* rand_helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rand_helpers.h; sourceTree = ""; }; + E11837012B081DA700637D44 /* rand_helpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rand_helpers.cpp; sourceTree = ""; }; + E11837022B081DA700637D44 /* hash.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hash.cpp; sourceTree = ""; }; + E11837032B081DA700637D44 /* threadsafecounter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = threadsafecounter.cpp; sourceTree = ""; }; + E11837042B081DA700637D44 /* datetime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = datetime.cpp; sourceTree = ""; }; + E11837052B081DA700637D44 /* global.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = global.cpp; sourceTree = ""; }; + E11837062B081DA700637D44 /* logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = logger.h; sourceTree = ""; }; + E11837072B081DA700637D44 /* commandloop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = commandloop.cpp; sourceTree = ""; }; + E11837082B081DA700637D44 /* threadsafequeue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = threadsafequeue.h; sourceTree = ""; }; + E11837092B081DA700637D44 /* hash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hash.h; sourceTree = ""; }; + E118370A2B081DA700637D44 /* throttle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = throttle.h; sourceTree = ""; }; + E118370B2B081DA700637D44 /* fancymath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fancymath.h; sourceTree = ""; }; + E118370C2B081DA700637D44 /* fancymath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fancymath.cpp; sourceTree = ""; }; + E11837152B081DA700637D44 /* testsearchcommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = testsearchcommon.h; sourceTree = ""; }; + E11837162B081DA700637D44 /* testbook.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testbook.cpp; sourceTree = ""; }; + E11837172B081DA700637D44 /* testrules.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testrules.cpp; sourceTree = ""; }; + E11837182B081DA700637D44 /* testtime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testtime.cpp; sourceTree = ""; }; + E11837192B081DA700637D44 /* testsgf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testsgf.cpp; sourceTree = ""; }; + E118371F2B081DA700637D44 /* testsearchv9.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testsearchv9.cpp; sourceTree = ""; }; + E11837202B081DA700637D44 /* tests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tests.h; sourceTree = ""; }; + E11837212B081DA700637D44 /* testsearchv8.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testsearchv8.cpp; sourceTree = ""; }; + E11837222B081DA700637D44 /* testsearchnonn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testsearchnonn.cpp; sourceTree = ""; }; + E11837232B081DA700637D44 /* testsearchcommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testsearchcommon.cpp; sourceTree = ""; }; + E11837242B081DA700637D44 /* tinymodel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tinymodel.cpp; sourceTree = ""; }; + E11837252B081DA700637D44 /* testcommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testcommon.cpp; sourceTree = ""; }; + E118373F2B081DA700637D44 /* testsymmetries.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testsymmetries.cpp; sourceTree = ""; }; + E11837402B081DA700637D44 /* tinymodeldata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tinymodeldata.cpp; sourceTree = ""; }; + E11837D02B081DA700637D44 /* testownership.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testownership.cpp; sourceTree = ""; }; + E11837D12B081DA700637D44 /* testnninputs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testnninputs.cpp; sourceTree = ""; }; + E11837D22B081DA700637D44 /* testsearchmisc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testsearchmisc.cpp; sourceTree = ""; }; + E11837D32B081DA700637D44 /* testtrainingwrite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testtrainingwrite.cpp; sourceTree = ""; }; + E11837D42B081DA700637D44 /* testscore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testscore.cpp; sourceTree = ""; }; + E11837D52B081DA700637D44 /* testboardarea.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testboardarea.cpp; sourceTree = ""; }; + E11837D62B081DA700637D44 /* testnn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testnn.cpp; sourceTree = ""; }; + E11837E32B081DA700637D44 /* testconfig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testconfig.cpp; sourceTree = ""; }; + E11837E42B081DA700637D44 /* testsearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testsearch.cpp; sourceTree = ""; }; + E11837F92B081DA700637D44 /* testsearchv3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testsearchv3.cpp; sourceTree = ""; }; + E11837FA2B081DA700637D44 /* testmisc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testmisc.cpp; sourceTree = ""; }; + E11837FB2B081DA700637D44 /* testnnevalcanary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testnnevalcanary.cpp; sourceTree = ""; }; + E11837FC2B081DA700637D44 /* testboardbasic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testboardbasic.cpp; sourceTree = ""; }; + E11837FD2B081DA700637D44 /* tinymodel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tinymodel.h; sourceTree = ""; }; + E11837FF2B081DA700637D44 /* desc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = desc.h; sourceTree = ""; }; + E11838002B081DA700637D44 /* coremlbackend.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = coremlbackend.cpp; sourceTree = ""; }; + E11838092B081DA700637D44 /* desc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = desc.cpp; sourceTree = ""; }; + E11838102B081DA700637D44 /* coremlbackend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coremlbackend.h; sourceTree = ""; }; + E11838112B081DA700637D44 /* openclhelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = openclhelpers.cpp; sourceTree = ""; }; + E11838122B081DA700637D44 /* metalbackend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metalbackend.h; sourceTree = ""; }; + E11838142B081DA700637D44 /* nninterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nninterface.h; sourceTree = ""; }; + E11838172B081DA700637D44 /* modelversion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = modelversion.cpp; sourceTree = ""; }; + E11838182B081DA700637D44 /* modelversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modelversion.h; sourceTree = ""; }; + E11838192B081DA700637D44 /* nninputs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nninputs.cpp; sourceTree = ""; }; + E118381A2B081DA700637D44 /* activations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = activations.h; sourceTree = ""; }; + E11838202B081DA700637D44 /* nninputs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nninputs.h; sourceTree = ""; }; + E11838242B081DA700637D44 /* nneval.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nneval.h; sourceTree = ""; }; + E11838252B081DA700637D44 /* metalbackend.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = metalbackend.cpp; sourceTree = ""; }; + E11838262B081DA700637D44 /* nneval.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nneval.cpp; sourceTree = ""; }; + E11838282B081DA700637D44 /* graphhash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = graphhash.h; sourceTree = ""; }; + E11838292B081DA700637D44 /* board.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = board.cpp; sourceTree = ""; }; + E118382A2B081DA700637D44 /* boardhistory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = boardhistory.cpp; sourceTree = ""; }; + E118382B2B081DA700637D44 /* rules.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rules.cpp; sourceTree = ""; }; + E118382C2B081DA700637D44 /* board.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = board.h; sourceTree = ""; }; + E118382D2B081DA700637D44 /* graphhash.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = graphhash.cpp; sourceTree = ""; }; + E118382E2B081DA700637D44 /* rules.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rules.h; sourceTree = ""; }; + E118382F2B081DA700637D44 /* boardhistory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = boardhistory.h; sourceTree = ""; }; + E11838312B081DA800637D44 /* analysisdata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = analysisdata.h; sourceTree = ""; }; + E11838322B081DA800637D44 /* searchparams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = searchparams.h; sourceTree = ""; }; + E11838332B081DA800637D44 /* timecontrols.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = timecontrols.cpp; sourceTree = ""; }; + E11838342B081DA800637D44 /* searchnodetable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = searchnodetable.cpp; sourceTree = ""; }; + E11838352B081DA800637D44 /* searchprint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = searchprint.h; sourceTree = ""; }; + E11838362B081DA800637D44 /* patternbonustable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = patternbonustable.cpp; sourceTree = ""; }; + E11838372B081DA800637D44 /* searchpuct.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = searchpuct.cpp; sourceTree = ""; }; + E11838382B081DA800637D44 /* subtreevaluebiastable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = subtreevaluebiastable.cpp; sourceTree = ""; }; + E11838392B081DA800637D44 /* asyncbot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = asyncbot.cpp; sourceTree = ""; }; + E118383A2B081DA800637D44 /* searchprint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = searchprint.cpp; sourceTree = ""; }; + E118383B2B081DA800637D44 /* searchresults.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = searchresults.cpp; sourceTree = ""; }; + E118383C2B081DA800637D44 /* reportedsearchvalues.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = reportedsearchvalues.h; sourceTree = ""; }; + E118383D2B081DA800637D44 /* localpattern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = localpattern.h; sourceTree = ""; }; + E118383E2B081DA800637D44 /* searchnode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = searchnode.cpp; sourceTree = ""; }; + E118383F2B081DA800637D44 /* mutexpool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mutexpool.cpp; sourceTree = ""; }; + E11838402B081DA800637D44 /* searchmirror.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = searchmirror.cpp; sourceTree = ""; }; + E11838412B081DA800637D44 /* reportedsearchvalues.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = reportedsearchvalues.cpp; sourceTree = ""; }; + E11838422B081DA800637D44 /* searchmultithreadhelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = searchmultithreadhelpers.cpp; sourceTree = ""; }; + E11838432B081DA800637D44 /* searchupdatehelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = searchupdatehelpers.cpp; sourceTree = ""; }; + E11838442B081DA800637D44 /* searchtimehelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = searchtimehelpers.cpp; sourceTree = ""; }; + E11838452B081DA800637D44 /* asyncbot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asyncbot.h; sourceTree = ""; }; + E11838462B081DA800637D44 /* localpattern.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = localpattern.cpp; sourceTree = ""; }; + E11838472B081DA800637D44 /* searchnodetable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = searchnodetable.h; sourceTree = ""; }; + E11838482B081DA800637D44 /* distributiontable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = distributiontable.h; sourceTree = ""; }; + E11838492B081DA800637D44 /* subtreevaluebiastable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = subtreevaluebiastable.h; sourceTree = ""; }; + E118384A2B081DA800637D44 /* search.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = search.cpp; sourceTree = ""; }; + E118384B2B081DA800637D44 /* analysisdata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = analysisdata.cpp; sourceTree = ""; }; + E118384C2B081DA800637D44 /* patternbonustable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = patternbonustable.h; sourceTree = ""; }; + E118384D2B081DA800637D44 /* searchhelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = searchhelpers.cpp; sourceTree = ""; }; + E118384E2B081DA800637D44 /* searchnnhelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = searchnnhelpers.cpp; sourceTree = ""; }; + E118384F2B081DA800637D44 /* mutexpool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mutexpool.h; sourceTree = ""; }; + E11838502B081DA800637D44 /* searchparams.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = searchparams.cpp; sourceTree = ""; }; + E11838512B081DA800637D44 /* search.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = search.h; sourceTree = ""; }; + E11838522B081DA800637D44 /* timecontrols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = timecontrols.h; sourceTree = ""; }; + E11838532B081DA800637D44 /* searchnode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = searchnode.h; sourceTree = ""; }; + E11838542B081DA800637D44 /* distributiontable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = distributiontable.cpp; sourceTree = ""; }; + E11838552B081DA800637D44 /* searchexplorehelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = searchexplorehelpers.cpp; sourceTree = ""; }; + E11838762B081DA800637D44 /* book.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = book.h; sourceTree = ""; }; + E11838772B081DA800637D44 /* bookcssjs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bookcssjs.cpp; sourceTree = ""; }; + E11838782B081DA800637D44 /* book.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = book.cpp; sourceTree = ""; }; + E118387A2B081DA800637D44 /* play.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = play.h; sourceTree = ""; }; + E118387B2B081DA800637D44 /* setup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = setup.h; sourceTree = ""; }; + E118387C2B081DA800637D44 /* play.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = play.cpp; sourceTree = ""; }; + E118387D2B081DA800637D44 /* playsettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = playsettings.h; sourceTree = ""; }; + E118387E2B081DA800637D44 /* selfplaymanager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = selfplaymanager.cpp; sourceTree = ""; }; + E118387F2B081DA800637D44 /* gtpconfig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gtpconfig.cpp; sourceTree = ""; }; + E11838802B081DA800637D44 /* setup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = setup.cpp; sourceTree = ""; }; + E11838812B081DA800637D44 /* playsettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = playsettings.cpp; sourceTree = ""; }; + E11838822B081DA800637D44 /* selfplaymanager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = selfplaymanager.h; sourceTree = ""; }; + E11838832B081DA800637D44 /* gtpconfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gtpconfig.h; sourceTree = ""; }; + E11838842B081DA800637D44 /* playutils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = playutils.h; sourceTree = ""; }; + E11838852B081DA800637D44 /* playutils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = playutils.cpp; sourceTree = ""; }; + E11838862B081DA800637D44 /* gitinfotemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gitinfotemplate.h; sourceTree = ""; }; + E11838892B081DA800637D44 /* genbook.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = genbook.cpp; sourceTree = ""; }; + E118388A2B081DA800637D44 /* analysis.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = analysis.cpp; sourceTree = ""; }; + E118388B2B081DA800637D44 /* gputest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gputest.cpp; sourceTree = ""; }; + E118388C2B081DA800637D44 /* runtests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = runtests.cpp; sourceTree = ""; }; + E118388D2B081DA800637D44 /* selfplay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = selfplay.cpp; sourceTree = ""; }; + E118388E2B081DA800637D44 /* misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = misc.cpp; sourceTree = ""; }; + E118388F2B081DA800637D44 /* sandbox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sandbox.cpp; sourceTree = ""; }; + E11838902B081DA800637D44 /* gtp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gtp.cpp; sourceTree = ""; }; + E11838912B081DA800637D44 /* gatekeeper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gatekeeper.cpp; sourceTree = ""; }; + E11838922B081DA800637D44 /* evalsgf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = evalsgf.cpp; sourceTree = ""; }; + E11838932B081DA800637D44 /* benchmark.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = benchmark.cpp; sourceTree = ""; }; + E11838942B081DA800637D44 /* match.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = match.cpp; sourceTree = ""; }; + E11838952B081DA800637D44 /* tune.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tune.cpp; sourceTree = ""; }; + E11838962B081DA800637D44 /* commandline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = commandline.h; sourceTree = ""; }; + E11838972B081DA800637D44 /* contribute.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = contribute.cpp; sourceTree = ""; }; + E11838982B081DA800637D44 /* commandline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = commandline.cpp; sourceTree = ""; }; + E1183E5F2B081DA900637D44 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; + E11887E12B0830C900637D44 /* KataGoSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KataGoSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E11887E32B0830C900637D44 /* KataGoSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KataGoSwift.h; sourceTree = ""; }; + E11887EC2B08310800637D44 /* coremlmodel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = coremlmodel.swift; path = ../../../cpp/neuralnet/coremlmodel.swift; sourceTree = ""; }; + E11887ED2B08310800637D44 /* coremlbackend.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = coremlbackend.swift; path = ../../../cpp/neuralnet/coremlbackend.swift; sourceTree = ""; }; + E11887EE2B08310800637D44 /* metalbackend.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = metalbackend.swift; path = ../../../cpp/neuralnet/metalbackend.swift; sourceTree = ""; }; + E118EE902B081C3200637D44 /* katago.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = katago.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E118EF0C2B081D8500637D44 /* main.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = main.h; sourceTree = ""; }; + E149B7F22B350EA8002B7F61 /* parallel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parallel.cpp; sourceTree = ""; }; + E149B7F32B350EA8002B7F61 /* parallel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parallel.h; sourceTree = ""; }; + E18F3E0D2A51466A00D335E1 /* KataGo iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "KataGo iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + E18F3E102A51466A00D335E1 /* KataGo_iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KataGo_iOSApp.swift; sourceTree = ""; }; + E18F3E122A51466A00D335E1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + E18F3E142A51466C00D335E1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + E18F3E172A51466C00D335E1 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + E18F3E1D2A51466C00D335E1 /* KataGo iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "KataGo iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + E18F3E212A51466C00D335E1 /* KataGo_iOSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KataGo_iOSTests.swift; sourceTree = ""; }; + E18F3E272A51466C00D335E1 /* KataGo iOSUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "KataGo iOSUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + E18F3E2B2A51466C00D335E1 /* KataGo_iOSUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KataGo_iOSUITests.swift; sourceTree = ""; }; + E18F3E2D2A51466C00D335E1 /* KataGo_iOSUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KataGo_iOSUITestsLaunchTests.swift; sourceTree = ""; }; + E18F3F712A5149AB00D335E1 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + E18F3F742A514B9700D335E1 /* default_model.bin.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = default_model.bin.gz; sourceTree = ""; }; + E18F3F752A514B9700D335E1 /* default_gtp.cfg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = default_gtp.cfg; sourceTree = ""; }; + E19D2E352AC8E5DB00C2A807 /* KataGoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KataGoModel.swift; sourceTree = ""; }; + E19D2E372AC97FA300C2A807 /* ToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarView.swift; sourceTree = ""; }; + E1A26B492B47684400BA922B /* KataGoModel29x29fp16.mlpackage */ = {isa = PBXFileReference; explicitFileType = wrapper.application; path = KataGoModel29x29fp16.mlpackage; sourceTree = ""; }; + E1B63BE32AABDF3500094965 /* BoardLineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoardLineView.swift; sourceTree = ""; }; + E1B922742A5179A7006D3137 /* KataGoHelper.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = KataGoHelper.mm; sourceTree = ""; }; + E1B922762A5179C6006D3137 /* KataGoHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KataGoHelper.h; sourceTree = ""; }; + E1C682702AA2A4E7001B4F44 /* GobanView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GobanView.swift; sourceTree = ""; }; + E1C682722AA2B122001B4F44 /* WoodView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WoodView.swift; sourceTree = ""; }; + E1C682742AA2CC31001B4F44 /* CommandView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandView.swift; sourceTree = ""; }; + E1D7D3AA2AA7547D00556DFB /* ButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonView.swift; sourceTree = ""; }; + E1D7D3AC2AA897C000556DFB /* StoneView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoneView.swift; sourceTree = ""; }; + E1D7D3B22AAA1F5600556DFB /* AnalysisView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalysisView.swift; sourceTree = ""; }; + E1E1717D2AB9DAED004DCC3C /* ConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigView.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E11887DE2B0830C900637D44 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E118EE8D2B081C3200637D44 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E11887F42B08312F00637D44 /* KataGoSwift.framework in Frameworks */, + E11887F52B0831B100637D44 /* libz.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E18F3E0A2A51466A00D335E1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E18F3F722A5149B300D335E1 /* libz.tbd in Frameworks */, + E11887E72B0830C900637D44 /* KataGoSwift.framework in Frameworks */, + E118EE962B081C3300637D44 /* katago.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E18F3E1A2A51466C00D335E1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E18F3E242A51466C00D335E1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E11836C92B081DA700637D44 /* dataio */ = { + isa = PBXGroup; + children = ( + E11836CA2B081DA700637D44 /* sgf.h */, + E11836CB2B081DA700637D44 /* trainingwrite.h */, + E11836CC2B081DA700637D44 /* homedata.h */, + E11836CD2B081DA700637D44 /* poswriter.cpp */, + E11836CE2B081DA700637D44 /* loadmodel.cpp */, + E11836CF2B081DA700637D44 /* trainingwrite.cpp */, + E11836D02B081DA700637D44 /* homedata.cpp */, + E11836D12B081DA700637D44 /* files.cpp */, + E11836D22B081DA700637D44 /* sgf.cpp */, + E11836D32B081DA700637D44 /* numpywrite.cpp */, + E11836D42B081DA700637D44 /* loadmodel.h */, + E11836D52B081DA700637D44 /* poswriter.h */, + E11836D62B081DA700637D44 /* files.h */, + E11836D72B081DA700637D44 /* numpywrite.h */, + ); + path = dataio; + sourceTree = ""; + }; + E11836D82B081DA700637D44 /* core */ = { + isa = PBXGroup; + children = ( + E149B7F22B350EA8002B7F61 /* parallel.cpp */, + E149B7F32B350EA8002B7F61 /* parallel.h */, + E11836D92B081DA700637D44 /* using.h */, + E11836DA2B081DA700637D44 /* md5.cpp */, + E11836DB2B081DA700637D44 /* multithread.cpp */, + E11836DC2B081DA700637D44 /* fileutils.h */, + E11836DD2B081DA700637D44 /* config_parser.cpp */, + E11836DE2B081DA700637D44 /* threadtest.cpp */, + E11836DF2B081DA700637D44 /* makedir.h */, + E11836E02B081DA700637D44 /* base64.h */, + E11836E12B081DA700637D44 /* config_parser.h */, + E11836E22B081DA700637D44 /* threadsafecounter.h */, + E11836E32B081DA700637D44 /* base64.cpp */, + E11836E42B081DA700637D44 /* elo.h */, + E11836E52B081DA700637D44 /* mainargs.h */, + E11836E62B081DA700637D44 /* global.h */, + E11836E72B081DA700637D44 /* threadtest.h */, + E11836E82B081DA700637D44 /* os.h */, + E11836E92B081DA700637D44 /* bsearch.h */, + E11836EA2B081DA700637D44 /* md5.h */, + E11836EB2B081DA700637D44 /* fileutils.cpp */, + E11836EC2B081DA700637D44 /* test.cpp */, + E11836ED2B081DA700637D44 /* timer.cpp */, + E11836EE2B081DA700637D44 /* test.h */, + E11836EF2B081DA700637D44 /* datetime.h */, + E11836F02B081DA700637D44 /* mainargs.cpp */, + E11836F12B081DA700637D44 /* multithread.h */, + E11836F22B081DA700637D44 /* sha2.cpp */, + E11836F32B081DA700637D44 /* commontypes.h */, + E11836F42B081DA700637D44 /* simpleallocator.h */, + E11836F52B081DA700637D44 /* timer.h */, + E11836F62B081DA700637D44 /* sha2.h */, + E11836F72B081DA700637D44 /* bsearch.cpp */, + E11836F82B081DA700637D44 /* rand.cpp */, + E11836F92B081DA700637D44 /* prioritymutex.h */, + E11836FA2B081DA700637D44 /* makedir.cpp */, + E11836FB2B081DA700637D44 /* elo.cpp */, + E11836FC2B081DA700637D44 /* rand.h */, + E11836FD2B081DA700637D44 /* threadsafequeue.cpp */, + E11836FE2B081DA700637D44 /* commandloop.h */, + E11836FF2B081DA700637D44 /* logger.cpp */, + E11837002B081DA700637D44 /* rand_helpers.h */, + E11837012B081DA700637D44 /* rand_helpers.cpp */, + E11837022B081DA700637D44 /* hash.cpp */, + E11837032B081DA700637D44 /* threadsafecounter.cpp */, + E11837042B081DA700637D44 /* datetime.cpp */, + E11837052B081DA700637D44 /* global.cpp */, + E11837062B081DA700637D44 /* logger.h */, + E11837072B081DA700637D44 /* commandloop.cpp */, + E11837082B081DA700637D44 /* threadsafequeue.h */, + E11837092B081DA700637D44 /* hash.h */, + E118370A2B081DA700637D44 /* throttle.h */, + E118370B2B081DA700637D44 /* fancymath.h */, + E118370C2B081DA700637D44 /* fancymath.cpp */, + ); + path = core; + sourceTree = ""; + }; + E11837142B081DA700637D44 /* tests */ = { + isa = PBXGroup; + children = ( + E11837152B081DA700637D44 /* testsearchcommon.h */, + E11837162B081DA700637D44 /* testbook.cpp */, + E11837172B081DA700637D44 /* testrules.cpp */, + E11837182B081DA700637D44 /* testtime.cpp */, + E11837192B081DA700637D44 /* testsgf.cpp */, + E118371F2B081DA700637D44 /* testsearchv9.cpp */, + E11837202B081DA700637D44 /* tests.h */, + E11837212B081DA700637D44 /* testsearchv8.cpp */, + E11837222B081DA700637D44 /* testsearchnonn.cpp */, + E11837232B081DA700637D44 /* testsearchcommon.cpp */, + E11837242B081DA700637D44 /* tinymodel.cpp */, + E11837252B081DA700637D44 /* testcommon.cpp */, + E118373F2B081DA700637D44 /* testsymmetries.cpp */, + E11837402B081DA700637D44 /* tinymodeldata.cpp */, + E11837D02B081DA700637D44 /* testownership.cpp */, + E11837D12B081DA700637D44 /* testnninputs.cpp */, + E11837D22B081DA700637D44 /* testsearchmisc.cpp */, + E11837D32B081DA700637D44 /* testtrainingwrite.cpp */, + E11837D42B081DA700637D44 /* testscore.cpp */, + E11837D52B081DA700637D44 /* testboardarea.cpp */, + E11837D62B081DA700637D44 /* testnn.cpp */, + E11837E32B081DA700637D44 /* testconfig.cpp */, + E11837E42B081DA700637D44 /* testsearch.cpp */, + E11837F92B081DA700637D44 /* testsearchv3.cpp */, + E11837FA2B081DA700637D44 /* testmisc.cpp */, + E11837FB2B081DA700637D44 /* testnnevalcanary.cpp */, + E11837FC2B081DA700637D44 /* testboardbasic.cpp */, + E11837FD2B081DA700637D44 /* tinymodel.h */, + ); + path = tests; + sourceTree = ""; + }; + E11837FE2B081DA700637D44 /* neuralnet */ = { + isa = PBXGroup; + children = ( + E11837FF2B081DA700637D44 /* desc.h */, + E11838002B081DA700637D44 /* coremlbackend.cpp */, + E11838092B081DA700637D44 /* desc.cpp */, + E11838102B081DA700637D44 /* coremlbackend.h */, + E11838112B081DA700637D44 /* openclhelpers.cpp */, + E11838122B081DA700637D44 /* metalbackend.h */, + E11838142B081DA700637D44 /* nninterface.h */, + E11838172B081DA700637D44 /* modelversion.cpp */, + E11838182B081DA700637D44 /* modelversion.h */, + E11838192B081DA700637D44 /* nninputs.cpp */, + E118381A2B081DA700637D44 /* activations.h */, + E11838202B081DA700637D44 /* nninputs.h */, + E11838242B081DA700637D44 /* nneval.h */, + E11838252B081DA700637D44 /* metalbackend.cpp */, + E11838262B081DA700637D44 /* nneval.cpp */, + ); + path = neuralnet; + sourceTree = ""; + }; + E11838272B081DA700637D44 /* game */ = { + isa = PBXGroup; + children = ( + E11838282B081DA700637D44 /* graphhash.h */, + E11838292B081DA700637D44 /* board.cpp */, + E118382A2B081DA700637D44 /* boardhistory.cpp */, + E118382B2B081DA700637D44 /* rules.cpp */, + E118382C2B081DA700637D44 /* board.h */, + E118382D2B081DA700637D44 /* graphhash.cpp */, + E118382E2B081DA700637D44 /* rules.h */, + E118382F2B081DA700637D44 /* boardhistory.h */, + ); + path = game; + sourceTree = ""; + }; + E11838302B081DA700637D44 /* search */ = { + isa = PBXGroup; + children = ( + E11838312B081DA800637D44 /* analysisdata.h */, + E11838322B081DA800637D44 /* searchparams.h */, + E11838332B081DA800637D44 /* timecontrols.cpp */, + E11838342B081DA800637D44 /* searchnodetable.cpp */, + E11838352B081DA800637D44 /* searchprint.h */, + E11838362B081DA800637D44 /* patternbonustable.cpp */, + E11838372B081DA800637D44 /* searchpuct.cpp */, + E11838382B081DA800637D44 /* subtreevaluebiastable.cpp */, + E11838392B081DA800637D44 /* asyncbot.cpp */, + E118383A2B081DA800637D44 /* searchprint.cpp */, + E118383B2B081DA800637D44 /* searchresults.cpp */, + E118383C2B081DA800637D44 /* reportedsearchvalues.h */, + E118383D2B081DA800637D44 /* localpattern.h */, + E118383E2B081DA800637D44 /* searchnode.cpp */, + E118383F2B081DA800637D44 /* mutexpool.cpp */, + E11838402B081DA800637D44 /* searchmirror.cpp */, + E11838412B081DA800637D44 /* reportedsearchvalues.cpp */, + E11838422B081DA800637D44 /* searchmultithreadhelpers.cpp */, + E11838432B081DA800637D44 /* searchupdatehelpers.cpp */, + E11838442B081DA800637D44 /* searchtimehelpers.cpp */, + E11838452B081DA800637D44 /* asyncbot.h */, + E11838462B081DA800637D44 /* localpattern.cpp */, + E11838472B081DA800637D44 /* searchnodetable.h */, + E11838482B081DA800637D44 /* distributiontable.h */, + E11838492B081DA800637D44 /* subtreevaluebiastable.h */, + E118384A2B081DA800637D44 /* search.cpp */, + E118384B2B081DA800637D44 /* analysisdata.cpp */, + E118384C2B081DA800637D44 /* patternbonustable.h */, + E118384D2B081DA800637D44 /* searchhelpers.cpp */, + E118384E2B081DA800637D44 /* searchnnhelpers.cpp */, + E118384F2B081DA800637D44 /* mutexpool.h */, + E11838502B081DA800637D44 /* searchparams.cpp */, + E11838512B081DA800637D44 /* search.h */, + E11838522B081DA800637D44 /* timecontrols.h */, + E11838532B081DA800637D44 /* searchnode.h */, + E11838542B081DA800637D44 /* distributiontable.cpp */, + E11838552B081DA800637D44 /* searchexplorehelpers.cpp */, + ); + path = search; + sourceTree = ""; + }; + E11838752B081DA800637D44 /* book */ = { + isa = PBXGroup; + children = ( + E11838762B081DA800637D44 /* book.h */, + E11838772B081DA800637D44 /* bookcssjs.cpp */, + E11838782B081DA800637D44 /* book.cpp */, + ); + path = book; + sourceTree = ""; + }; + E11838792B081DA800637D44 /* program */ = { + isa = PBXGroup; + children = ( + E118387A2B081DA800637D44 /* play.h */, + E118387B2B081DA800637D44 /* setup.h */, + E118387C2B081DA800637D44 /* play.cpp */, + E118387D2B081DA800637D44 /* playsettings.h */, + E118387E2B081DA800637D44 /* selfplaymanager.cpp */, + E118387F2B081DA800637D44 /* gtpconfig.cpp */, + E11838802B081DA800637D44 /* setup.cpp */, + E11838812B081DA800637D44 /* playsettings.cpp */, + E11838822B081DA800637D44 /* selfplaymanager.h */, + E11838832B081DA800637D44 /* gtpconfig.h */, + E11838842B081DA800637D44 /* playutils.h */, + E11838852B081DA800637D44 /* playutils.cpp */, + E11838862B081DA800637D44 /* gitinfotemplate.h */, + ); + path = program; + sourceTree = ""; + }; + E11838882B081DA800637D44 /* command */ = { + isa = PBXGroup; + children = ( + E11838892B081DA800637D44 /* genbook.cpp */, + E118388A2B081DA800637D44 /* analysis.cpp */, + E118388B2B081DA800637D44 /* gputest.cpp */, + E118388C2B081DA800637D44 /* runtests.cpp */, + E118388D2B081DA800637D44 /* selfplay.cpp */, + E118388E2B081DA800637D44 /* misc.cpp */, + E118388F2B081DA800637D44 /* sandbox.cpp */, + E11838902B081DA800637D44 /* gtp.cpp */, + E11838912B081DA800637D44 /* gatekeeper.cpp */, + E11838922B081DA800637D44 /* evalsgf.cpp */, + E11838932B081DA800637D44 /* benchmark.cpp */, + E11838942B081DA800637D44 /* match.cpp */, + E11838952B081DA800637D44 /* tune.cpp */, + E11838962B081DA800637D44 /* commandline.h */, + E11838972B081DA800637D44 /* contribute.cpp */, + E11838982B081DA800637D44 /* commandline.cpp */, + ); + path = command; + sourceTree = ""; + }; + E11887E22B0830C900637D44 /* KataGoSwift */ = { + isa = PBXGroup; + children = ( + E11887ED2B08310800637D44 /* coremlbackend.swift */, + E11887EC2B08310800637D44 /* coremlmodel.swift */, + E11887EE2B08310800637D44 /* metalbackend.swift */, + E11887E32B0830C900637D44 /* KataGoSwift.h */, + ); + path = KataGoSwift; + sourceTree = ""; + }; + E118EE912B081C3300637D44 /* katago */ = { + isa = PBXGroup; + children = ( + E118EF0B2B081D8500637D44 /* cpp */, + ); + name = katago; + path = ../..; + sourceTree = ""; + }; + E118EF0B2B081D8500637D44 /* cpp */ = { + isa = PBXGroup; + children = ( + E1183E5F2B081DA900637D44 /* main.cpp */, + E118EF0C2B081D8500637D44 /* main.h */, + E11838752B081DA800637D44 /* book */, + E11838882B081DA800637D44 /* command */, + E11836D82B081DA700637D44 /* core */, + E11836C92B081DA700637D44 /* dataio */, + E11838272B081DA700637D44 /* game */, + E11837FE2B081DA700637D44 /* neuralnet */, + E11838792B081DA800637D44 /* program */, + E11838302B081DA700637D44 /* search */, + E11837142B081DA700637D44 /* tests */, + ); + path = cpp; + sourceTree = ""; + }; + E18F3E042A51466A00D335E1 = { + isa = PBXGroup; + children = ( + E18F3F792A514BA700D335E1 /* Resources */, + E18F3E0F2A51466A00D335E1 /* KataGo iOS */, + E18F3E202A51466C00D335E1 /* KataGo iOSTests */, + E18F3E2A2A51466C00D335E1 /* KataGo iOSUITests */, + E118EE912B081C3300637D44 /* katago */, + E11887E22B0830C900637D44 /* KataGoSwift */, + E18F3E0E2A51466A00D335E1 /* Products */, + E18F3F702A5149AB00D335E1 /* Frameworks */, + ); + sourceTree = ""; + }; + E18F3E0E2A51466A00D335E1 /* Products */ = { + isa = PBXGroup; + children = ( + E18F3E0D2A51466A00D335E1 /* KataGo iOS.app */, + E18F3E1D2A51466C00D335E1 /* KataGo iOSTests.xctest */, + E18F3E272A51466C00D335E1 /* KataGo iOSUITests.xctest */, + E118EE902B081C3200637D44 /* katago.framework */, + E11887E12B0830C900637D44 /* KataGoSwift.framework */, + ); + name = Products; + sourceTree = ""; + }; + E18F3E0F2A51466A00D335E1 /* KataGo iOS */ = { + isa = PBXGroup; + children = ( + E18F3E142A51466C00D335E1 /* Assets.xcassets */, + E18F3E122A51466A00D335E1 /* ContentView.swift */, + E1C682702AA2A4E7001B4F44 /* GobanView.swift */, + E18F3E102A51466A00D335E1 /* KataGo_iOSApp.swift */, + E1B922762A5179C6006D3137 /* KataGoHelper.h */, + E1B922742A5179A7006D3137 /* KataGoHelper.mm */, + E18F3E162A51466C00D335E1 /* Preview Content */, + E1C682722AA2B122001B4F44 /* WoodView.swift */, + E1C682742AA2CC31001B4F44 /* CommandView.swift */, + E1D7D3AA2AA7547D00556DFB /* ButtonView.swift */, + E1D7D3AC2AA897C000556DFB /* StoneView.swift */, + E1D7D3B22AAA1F5600556DFB /* AnalysisView.swift */, + E1B63BE32AABDF3500094965 /* BoardLineView.swift */, + E1E1717D2AB9DAED004DCC3C /* ConfigView.swift */, + E19D2E352AC8E5DB00C2A807 /* KataGoModel.swift */, + E19D2E372AC97FA300C2A807 /* ToolbarView.swift */, + ); + path = "KataGo iOS"; + sourceTree = ""; + }; + E18F3E162A51466C00D335E1 /* Preview Content */ = { + isa = PBXGroup; + children = ( + E18F3E172A51466C00D335E1 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + E18F3E202A51466C00D335E1 /* KataGo iOSTests */ = { + isa = PBXGroup; + children = ( + E18F3E212A51466C00D335E1 /* KataGo_iOSTests.swift */, + ); + path = "KataGo iOSTests"; + sourceTree = ""; + }; + E18F3E2A2A51466C00D335E1 /* KataGo iOSUITests */ = { + isa = PBXGroup; + children = ( + E18F3E2B2A51466C00D335E1 /* KataGo_iOSUITests.swift */, + E18F3E2D2A51466C00D335E1 /* KataGo_iOSUITestsLaunchTests.swift */, + ); + path = "KataGo iOSUITests"; + sourceTree = ""; + }; + E18F3F702A5149AB00D335E1 /* Frameworks */ = { + isa = PBXGroup; + children = ( + E18F3F712A5149AB00D335E1 /* libz.tbd */, + ); + name = Frameworks; + sourceTree = ""; + }; + E18F3F792A514BA700D335E1 /* Resources */ = { + isa = PBXGroup; + children = ( + E1A26B492B47684400BA922B /* KataGoModel29x29fp16.mlpackage */, + E18F3F752A514B9700D335E1 /* default_gtp.cfg */, + E18F3F742A514B9700D335E1 /* default_model.bin.gz */, + ); + path = Resources; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + E11887DC2B0830C900637D44 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + E11887E42B0830C900637D44 /* KataGoSwift.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E118EE8B2B081C3200637D44 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + E118814B2B081E3D00637D44 /* desc.h in Headers */, + E11880422B081E3900637D44 /* makedir.h in Headers */, + E118817A2B081E3E00637D44 /* analysisdata.h in Headers */, + E11880392B081E3900637D44 /* poswriter.h in Headers */, + E11880382B081E3900637D44 /* loadmodel.h in Headers */, + E11881792B081E3E00637D44 /* boardhistory.h in Headers */, + E118819A2B081E3E00637D44 /* search.h in Headers */, + E11881852B081E3E00637D44 /* reportedsearchvalues.h in Headers */, + E118803A2B081E3900637D44 /* files.h in Headers */, + E118816B2B081E3E00637D44 /* nninputs.h in Headers */, + E11880612B081E3900637D44 /* commandloop.h in Headers */, + E11881632B081E3E00637D44 /* modelversion.h in Headers */, + E11881912B081E3E00637D44 /* distributiontable.h in Headers */, + E11880472B081E3900637D44 /* elo.h in Headers */, + E118803B2B081E3900637D44 /* numpywrite.h in Headers */, + E11880562B081E3900637D44 /* commontypes.h in Headers */, + E11880762B081E3A00637D44 /* testsearchcommon.h in Headers */, + E149B7F52B350EA8002B7F61 /* parallel.h in Headers */, + E11880802B081E3A00637D44 /* tests.h in Headers */, + E118815F2B081E3E00637D44 /* nninterface.h in Headers */, + E118802E2B081E3900637D44 /* sgf.h in Headers */, + E11880442B081E3900637D44 /* config_parser.h in Headers */, + E118805F2B081E3900637D44 /* rand.h in Headers */, + E118804B2B081E3900637D44 /* os.h in Headers */, + E11880482B081E3900637D44 /* mainargs.h in Headers */, + E118806C2B081E3900637D44 /* hash.h in Headers */, + E11881C62B081E3F00637D44 /* gtpconfig.h in Headers */, + E11881BA2B081E3F00637D44 /* book.h in Headers */, + E11880572B081E3900637D44 /* simpleallocator.h in Headers */, + E118804A2B081E3900637D44 /* threadtest.h in Headers */, + E118818E2B081E3E00637D44 /* asyncbot.h in Headers */, + E11880452B081E3900637D44 /* threadsafecounter.h in Headers */, + E11881C52B081E3F00637D44 /* selfplaymanager.h in Headers */, + E118819B2B081E3E00637D44 /* timecontrols.h in Headers */, + E11881952B081E3E00637D44 /* patternbonustable.h in Headers */, + E11881BD2B081E3F00637D44 /* play.h in Headers */, + E118817E2B081E3E00637D44 /* searchprint.h in Headers */, + E118803C2B081E3900637D44 /* using.h in Headers */, + E118806B2B081E3900637D44 /* threadsafequeue.h in Headers */, + E118804C2B081E3900637D44 /* bsearch.h in Headers */, + E11880542B081E3900637D44 /* multithread.h in Headers */, + E11881D82B081E3F00637D44 /* commandline.h in Headers */, + E118806D2B081E3900637D44 /* throttle.h in Headers */, + E11881C72B081E3F00637D44 /* playutils.h in Headers */, + E118803F2B081E3900637D44 /* fileutils.h in Headers */, + E11881782B081E3E00637D44 /* rules.h in Headers */, + E11881C92B081E3F00637D44 /* gitinfotemplate.h in Headers */, + E11881652B081E3E00637D44 /* activations.h in Headers */, + E11880692B081E3900637D44 /* logger.h in Headers */, + E11880582B081E3900637D44 /* timer.h in Headers */, + E11880522B081E3900637D44 /* datetime.h in Headers */, + E11881BE2B081E3F00637D44 /* setup.h in Headers */, + E118806E2B081E3900637D44 /* fancymath.h in Headers */, + E118816F2B081E3E00637D44 /* nneval.h in Headers */, + E11881762B081E3E00637D44 /* board.h in Headers */, + E118817B2B081E3E00637D44 /* searchparams.h in Headers */, + E118804D2B081E3900637D44 /* md5.h in Headers */, + E11880432B081E3900637D44 /* base64.h in Headers */, + E11881722B081E3E00637D44 /* graphhash.h in Headers */, + E118802F2B081E3900637D44 /* trainingwrite.h in Headers */, + E11880592B081E3900637D44 /* sha2.h in Headers */, + E118815B2B081E3E00637D44 /* coremlbackend.h in Headers */, + E11881982B081E3E00637D44 /* mutexpool.h in Headers */, + E11881922B081E3E00637D44 /* subtreevaluebiastable.h in Headers */, + E118814A2B081E3D00637D44 /* tinymodel.h in Headers */, + E1183E662B081DAA00637D44 /* main.h in Headers */, + E11880632B081E3900637D44 /* rand_helpers.h in Headers */, + E11881C02B081E3F00637D44 /* playsettings.h in Headers */, + E118815D2B081E3E00637D44 /* metalbackend.h in Headers */, + E11880512B081E3900637D44 /* test.h in Headers */, + E118805C2B081E3900637D44 /* prioritymutex.h in Headers */, + E11881902B081E3E00637D44 /* searchnodetable.h in Headers */, + E11881862B081E3E00637D44 /* localpattern.h in Headers */, + E11880492B081E3900637D44 /* global.h in Headers */, + E118819C2B081E3E00637D44 /* searchnode.h in Headers */, + E11880302B081E3900637D44 /* homedata.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + E11887E02B0830C900637D44 /* KataGoSwift */ = { + isa = PBXNativeTarget; + buildConfigurationList = E11887E92B0830C900637D44 /* Build configuration list for PBXNativeTarget "KataGoSwift" */; + buildPhases = ( + E11887DC2B0830C900637D44 /* Headers */, + E11887DD2B0830C900637D44 /* Sources */, + E11887DE2B0830C900637D44 /* Frameworks */, + E11887DF2B0830C900637D44 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = KataGoSwift; + productName = KataGoSwift; + productReference = E11887E12B0830C900637D44 /* KataGoSwift.framework */; + productType = "com.apple.product-type.framework"; + }; + E118EE8F2B081C3200637D44 /* katago */ = { + isa = PBXNativeTarget; + buildConfigurationList = E118EE982B081C3300637D44 /* Build configuration list for PBXNativeTarget "katago" */; + buildPhases = ( + E118EE8B2B081C3200637D44 /* Headers */, + E118EE8C2B081C3200637D44 /* Sources */, + E118EE8D2B081C3200637D44 /* Frameworks */, + E118EE8E2B081C3200637D44 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + E11887F32B08312600637D44 /* PBXTargetDependency */, + ); + name = katago; + productName = katago; + productReference = E118EE902B081C3200637D44 /* katago.framework */; + productType = "com.apple.product-type.framework"; + }; + E18F3E0C2A51466A00D335E1 /* KataGo iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = E18F3E312A51466C00D335E1 /* Build configuration list for PBXNativeTarget "KataGo iOS" */; + buildPhases = ( + E18F3E092A51466A00D335E1 /* Sources */, + E18F3E0A2A51466A00D335E1 /* Frameworks */, + E18F3E0B2A51466A00D335E1 /* Resources */, + E118EE842B0819E500637D44 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + E118EE952B081C3300637D44 /* PBXTargetDependency */, + E11887E62B0830C900637D44 /* PBXTargetDependency */, + ); + name = "KataGo iOS"; + productName = "KataGo iOS"; + productReference = E18F3E0D2A51466A00D335E1 /* KataGo iOS.app */; + productType = "com.apple.product-type.application"; + }; + E18F3E1C2A51466C00D335E1 /* KataGo iOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = E18F3E342A51466C00D335E1 /* Build configuration list for PBXNativeTarget "KataGo iOSTests" */; + buildPhases = ( + E18F3E192A51466C00D335E1 /* Sources */, + E18F3E1A2A51466C00D335E1 /* Frameworks */, + E18F3E1B2A51466C00D335E1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + E18F3E1F2A51466C00D335E1 /* PBXTargetDependency */, + ); + name = "KataGo iOSTests"; + productName = "KataGo iOSTests"; + productReference = E18F3E1D2A51466C00D335E1 /* KataGo iOSTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + E18F3E262A51466C00D335E1 /* KataGo iOSUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = E18F3E372A51466C00D335E1 /* Build configuration list for PBXNativeTarget "KataGo iOSUITests" */; + buildPhases = ( + E18F3E232A51466C00D335E1 /* Sources */, + E18F3E242A51466C00D335E1 /* Frameworks */, + E18F3E252A51466C00D335E1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + E18F3E292A51466C00D335E1 /* PBXTargetDependency */, + ); + name = "KataGo iOSUITests"; + productName = "KataGo iOSUITests"; + productReference = E18F3E272A51466C00D335E1 /* KataGo iOSUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E18F3E052A51466A00D335E1 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1510; + TargetAttributes = { + E11887E02B0830C900637D44 = { + CreatedOnToolsVersion = 15.0.1; + LastSwiftMigration = 1500; + }; + E118EE8F2B081C3200637D44 = { + CreatedOnToolsVersion = 15.0.1; + }; + E18F3E0C2A51466A00D335E1 = { + CreatedOnToolsVersion = 14.3.1; + LastSwiftMigration = 1430; + }; + E18F3E1C2A51466C00D335E1 = { + CreatedOnToolsVersion = 14.3.1; + TestTargetID = E18F3E0C2A51466A00D335E1; + }; + E18F3E262A51466C00D335E1 = { + CreatedOnToolsVersion = 14.3.1; + TestTargetID = E18F3E0C2A51466A00D335E1; + }; + }; + }; + buildConfigurationList = E18F3E082A51466A00D335E1 /* Build configuration list for PBXProject "KataGo iOS" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = E18F3E042A51466A00D335E1; + productRefGroup = E18F3E0E2A51466A00D335E1 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E18F3E0C2A51466A00D335E1 /* KataGo iOS */, + E18F3E1C2A51466C00D335E1 /* KataGo iOSTests */, + E18F3E262A51466C00D335E1 /* KataGo iOSUITests */, + E118EE8F2B081C3200637D44 /* katago */, + E11887E02B0830C900637D44 /* KataGoSwift */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E11887DF2B0830C900637D44 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E118EE8E2B081C3200637D44 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E18F3E0B2A51466A00D335E1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E1A26B4B2B47693300BA922B /* KataGoModel29x29fp16.mlpackage in Resources */, + E18F3F782A514B9700D335E1 /* default_gtp.cfg in Resources */, + E18F3E182A51466C00D335E1 /* Preview Assets.xcassets in Resources */, + E18F3E152A51466C00D335E1 /* Assets.xcassets in Resources */, + E18F3F772A514B9700D335E1 /* default_model.bin.gz in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E18F3E1B2A51466C00D335E1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E18F3E252A51466C00D335E1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E11887DD2B0830C900637D44 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E11887F02B08310800637D44 /* coremlbackend.swift in Sources */, + E11887F12B08310800637D44 /* metalbackend.swift in Sources */, + E11887EF2B08310800637D44 /* coremlmodel.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E118EE8C2B081C3200637D44 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E11880852B081E3A00637D44 /* testcommon.cpp in Sources */, + E11881C22B081E3F00637D44 /* gtpconfig.cpp in Sources */, + E118817F2B081E3E00637D44 /* patternbonustable.cpp in Sources */, + E118806A2B081E3900637D44 /* commandloop.cpp in Sources */, + E11880682B081E3900637D44 /* global.cpp in Sources */, + E11881872B081E3E00637D44 /* searchnode.cpp in Sources */, + E118805A2B081E3900637D44 /* bsearch.cpp in Sources */, + E11880532B081E3900637D44 /* mainargs.cpp in Sources */, + E149B7F42B350EA8002B7F61 /* parallel.cpp in Sources */, + E11880992B081E3A00637D44 /* tinymodeldata.cpp in Sources */, + E11881812B081E3E00637D44 /* subtreevaluebiastable.cpp in Sources */, + E11880322B081E3900637D44 /* loadmodel.cpp in Sources */, + E11880602B081E3900637D44 /* threadsafequeue.cpp in Sources */, + E11881262B081E3D00637D44 /* testscore.cpp in Sources */, + E11881D42B081E3F00637D44 /* evalsgf.cpp in Sources */, + E11881C82B081E3F00637D44 /* playutils.cpp in Sources */, + E11880552B081E3900637D44 /* sha2.cpp in Sources */, + E11881CE2B081E3F00637D44 /* runtests.cpp in Sources */, + E11881712B081E3E00637D44 /* nneval.cpp in Sources */, + E11880652B081E3900637D44 /* hash.cpp in Sources */, + E11881D32B081E3F00637D44 /* gatekeeper.cpp in Sources */, + E11881992B081E3E00637D44 /* searchparams.cpp in Sources */, + E118818A2B081E3E00637D44 /* reportedsearchvalues.cpp in Sources */, + E11881482B081E3D00637D44 /* testnnevalcanary.cpp in Sources */, + E11880412B081E3900637D44 /* threadtest.cpp in Sources */, + E11881752B081E3E00637D44 /* rules.cpp in Sources */, + E11880312B081E3900637D44 /* poswriter.cpp in Sources */, + E11881D02B081E3F00637D44 /* misc.cpp in Sources */, + E11881CF2B081E3F00637D44 /* selfplay.cpp in Sources */, + E11880402B081E3900637D44 /* config_parser.cpp in Sources */, + E11881342B081E3D00637D44 /* testconfig.cpp in Sources */, + E11881222B081E3D00637D44 /* testownership.cpp in Sources */, + E118818F2B081E3E00637D44 /* localpattern.cpp in Sources */, + E11880362B081E3900637D44 /* sgf.cpp in Sources */, + E11881DA2B081E3F00637D44 /* commandline.cpp in Sources */, + E118819D2B081E3E00637D44 /* distributiontable.cpp in Sources */, + E11881492B081E3D00637D44 /* testboardbasic.cpp in Sources */, + E11881D22B081E3F00637D44 /* gtp.cpp in Sources */, + E11881972B081E3E00637D44 /* searchnnhelpers.cpp in Sources */, + E118815C2B081E3E00637D44 /* openclhelpers.cpp in Sources */, + E11881832B081E3E00637D44 /* searchprint.cpp in Sources */, + E11881232B081E3D00637D44 /* testnninputs.cpp in Sources */, + E11881BB2B081E3F00637D44 /* bookcssjs.cpp in Sources */, + E11881802B081E3E00637D44 /* searchpuct.cpp in Sources */, + E11881542B081E3E00637D44 /* desc.cpp in Sources */, + E118804F2B081E3900637D44 /* test.cpp in Sources */, + E118819E2B081E3E00637D44 /* searchexplorehelpers.cpp in Sources */, + E11881252B081E3D00637D44 /* testtrainingwrite.cpp in Sources */, + E11881D12B081E3F00637D44 /* sandbox.cpp in Sources */, + E11881CB2B081E3F00637D44 /* genbook.cpp in Sources */, + E11880372B081E3900637D44 /* numpywrite.cpp in Sources */, + E11881D92B081E3F00637D44 /* contribute.cpp in Sources */, + E11881472B081E3D00637D44 /* testmisc.cpp in Sources */, + E11880832B081E3A00637D44 /* testsearchcommon.cpp in Sources */, + E11881BF2B081E3F00637D44 /* play.cpp in Sources */, + E11881842B081E3E00637D44 /* searchresults.cpp in Sources */, + E11880792B081E3A00637D44 /* testtime.cpp in Sources */, + E11880642B081E3900637D44 /* rand_helpers.cpp in Sources */, + E118814C2B081E3D00637D44 /* coremlbackend.cpp in Sources */, + E11881772B081E3E00637D44 /* graphhash.cpp in Sources */, + E11881702B081E3E00637D44 /* metalbackend.cpp in Sources */, + E118803D2B081E3900637D44 /* md5.cpp in Sources */, + E11881C32B081E3F00637D44 /* setup.cpp in Sources */, + E11881272B081E3D00637D44 /* testboardarea.cpp in Sources */, + E118805D2B081E3900637D44 /* makedir.cpp in Sources */, + E11880842B081E3A00637D44 /* tinymodel.cpp in Sources */, + E11881BC2B081E3F00637D44 /* book.cpp in Sources */, + E11881942B081E3E00637D44 /* analysisdata.cpp in Sources */, + E11880812B081E3A00637D44 /* testsearchv8.cpp in Sources */, + E11881C42B081E3F00637D44 /* playsettings.cpp in Sources */, + E11880352B081E3900637D44 /* files.cpp in Sources */, + E118817D2B081E3E00637D44 /* searchnodetable.cpp in Sources */, + E11881CC2B081E3F00637D44 /* analysis.cpp in Sources */, + E11880782B081E3A00637D44 /* testrules.cpp in Sources */, + E11880342B081E3900637D44 /* homedata.cpp in Sources */, + E11880462B081E3900637D44 /* base64.cpp in Sources */, + E11881282B081E3D00637D44 /* testnn.cpp in Sources */, + E11880772B081E3A00637D44 /* testbook.cpp in Sources */, + E11880672B081E3900637D44 /* datetime.cpp in Sources */, + E11880822B081E3A00637D44 /* testsearchnonn.cpp in Sources */, + E11880662B081E3900637D44 /* threadsafecounter.cpp in Sources */, + E11881822B081E3E00637D44 /* asyncbot.cpp in Sources */, + E11881462B081E3D00637D44 /* testsearchv3.cpp in Sources */, + E11881C12B081E3F00637D44 /* selfplaymanager.cpp in Sources */, + E118806F2B081E3900637D44 /* fancymath.cpp in Sources */, + E118807F2B081E3A00637D44 /* testsearchv9.cpp in Sources */, + E11881242B081E3D00637D44 /* testsearchmisc.cpp in Sources */, + E11881732B081E3E00637D44 /* board.cpp in Sources */, + E11887632B081E4E00637D44 /* main.cpp in Sources */, + E11881D62B081E3F00637D44 /* match.cpp in Sources */, + E11880622B081E3900637D44 /* logger.cpp in Sources */, + E11881742B081E3E00637D44 /* boardhistory.cpp in Sources */, + E11880332B081E3900637D44 /* trainingwrite.cpp in Sources */, + E11881CD2B081E3F00637D44 /* gputest.cpp in Sources */, + E11881352B081E3D00637D44 /* testsearch.cpp in Sources */, + E11880982B081E3A00637D44 /* testsymmetries.cpp in Sources */, + E118818C2B081E3E00637D44 /* searchupdatehelpers.cpp in Sources */, + E11881932B081E3E00637D44 /* search.cpp in Sources */, + E11881962B081E3E00637D44 /* searchhelpers.cpp in Sources */, + E118805B2B081E3900637D44 /* rand.cpp in Sources */, + E118804E2B081E3900637D44 /* fileutils.cpp in Sources */, + E118818D2B081E3E00637D44 /* searchtimehelpers.cpp in Sources */, + E118817C2B081E3E00637D44 /* timecontrols.cpp in Sources */, + E11880502B081E3900637D44 /* timer.cpp in Sources */, + E11881882B081E3E00637D44 /* mutexpool.cpp in Sources */, + E11881D52B081E3F00637D44 /* benchmark.cpp in Sources */, + E118818B2B081E3E00637D44 /* searchmultithreadhelpers.cpp in Sources */, + E118807A2B081E3A00637D44 /* testsgf.cpp in Sources */, + E11881622B081E3E00637D44 /* modelversion.cpp in Sources */, + E11881642B081E3E00637D44 /* nninputs.cpp in Sources */, + E118803E2B081E3900637D44 /* multithread.cpp in Sources */, + E118805E2B081E3900637D44 /* elo.cpp in Sources */, + E11881892B081E3E00637D44 /* searchmirror.cpp in Sources */, + E11881D72B081E3F00637D44 /* tune.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E18F3E092A51466A00D335E1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E1D7D3AB2AA7547D00556DFB /* ButtonView.swift in Sources */, + E18F3E132A51466A00D335E1 /* ContentView.swift in Sources */, + E19D2E362AC8E5DB00C2A807 /* KataGoModel.swift in Sources */, + E1D7D3AD2AA897C000556DFB /* StoneView.swift in Sources */, + E1D7D3B32AAA1F5600556DFB /* AnalysisView.swift in Sources */, + E18F3E112A51466A00D335E1 /* KataGo_iOSApp.swift in Sources */, + E19D2E382AC97FA300C2A807 /* ToolbarView.swift in Sources */, + E1C682712AA2A4E7001B4F44 /* GobanView.swift in Sources */, + E1C682752AA2CC31001B4F44 /* CommandView.swift in Sources */, + E1B63BE42AABDF3500094965 /* BoardLineView.swift in Sources */, + E1C682732AA2B122001B4F44 /* WoodView.swift in Sources */, + E1B922752A5179A7006D3137 /* KataGoHelper.mm in Sources */, + E1E1717E2AB9DAED004DCC3C /* ConfigView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E18F3E192A51466C00D335E1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E18F3E222A51466C00D335E1 /* KataGo_iOSTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E18F3E232A51466C00D335E1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E18F3E2C2A51466C00D335E1 /* KataGo_iOSUITests.swift in Sources */, + E18F3E2E2A51466C00D335E1 /* KataGo_iOSUITestsLaunchTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + E11887E62B0830C900637D44 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E11887E02B0830C900637D44 /* KataGoSwift */; + targetProxy = E11887E52B0830C900637D44 /* PBXContainerItemProxy */; + }; + E11887F32B08312600637D44 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E11887E02B0830C900637D44 /* KataGoSwift */; + targetProxy = E11887F22B08312600637D44 /* PBXContainerItemProxy */; + }; + E118EE952B081C3300637D44 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E118EE8F2B081C3200637D44 /* katago */; + targetProxy = E118EE942B081C3300637D44 /* PBXContainerItemProxy */; + }; + E18F3E1F2A51466C00D335E1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E18F3E0C2A51466A00D335E1 /* KataGo iOS */; + targetProxy = E18F3E1E2A51466C00D335E1 /* PBXContainerItemProxy */; + }; + E18F3E292A51466C00D335E1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E18F3E0C2A51466A00D335E1 /* KataGo iOS */; + targetProxy = E18F3E282A51466C00D335E1 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + E11887EA2B0830C900637D44 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 4L5BJK5M8K; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = ccy.KataGoSwift; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + E11887EB2B0830C900637D44 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 4L5BJK5M8K; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = ccy.KataGoSwift; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + E118EE992B081C3300637D44 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = 4L5BJK5M8K; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = ( + "../../cpp/external/tclap-1.2.2/include", + ../../cpp/external, + ); + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = ccy.katago; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + SYSTEM_HEADER_SEARCH_PATHS = "../../cpp/external/filesystem-1.5.8/include"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + E118EE9A2B081C3300637D44 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = 4L5BJK5M8K; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = ( + "../../cpp/external/tclap-1.2.2/include", + ../../cpp/external, + ); + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = ccy.katago; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + SYSTEM_HEADER_SEARCH_PATHS = "../../cpp/external/filesystem-1.5.8/include"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + E18F3E2F2A51466C00D335E1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + USE_COREML_BACKEND, + NO_LIBZIP, + NO_GIT_REVISION, + OS_IS_IOS, + "COMPILE_MAX_BOARD_LEN=29", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OBJC_INTEROP_MODE = objcxx; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + E18F3E302A51466C00D335E1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + USE_COREML_BACKEND, + NO_LIBZIP, + NO_GIT_REVISION, + OS_IS_IOS, + "COMPILE_MAX_BOARD_LEN=29", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OBJC_INTEROP_MODE = objcxx; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + E18F3E322A51466C00D335E1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"KataGo iOS/Preview Content\""; + DEVELOPMENT_TEAM = 4L5BJK5M8K; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = ( + "../../cpp/external/tclap-1.2.2/include", + ../../cpp/external, + ); + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "ccy.KataGo-iOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = ../../cpp/neuralnet/metalbridge.h; + SWIFT_OBJC_INTERFACE_HEADER_NAME = metalswift.h; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + SYSTEM_HEADER_SEARCH_PATHS = "../../cpp/external/filesystem-1.5.8/include"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + E18F3E332A51466C00D335E1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"KataGo iOS/Preview Content\""; + DEVELOPMENT_TEAM = 4L5BJK5M8K; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = ( + "../../cpp/external/tclap-1.2.2/include", + ../../cpp/external, + ); + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "ccy.KataGo-iOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = ../../cpp/neuralnet/metalbridge.h; + SWIFT_OBJC_INTERFACE_HEADER_NAME = metalswift.h; + SWIFT_VERSION = 5.0; + SYSTEM_HEADER_SEARCH_PATHS = "../../cpp/external/filesystem-1.5.8/include"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + E18F3E352A51466C00D335E1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4L5BJK5M8K; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "ccy.KataGo-iOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/KataGo iOS.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/KataGo iOS"; + }; + name = Debug; + }; + E18F3E362A51466C00D335E1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4L5BJK5M8K; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "ccy.KataGo-iOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/KataGo iOS.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/KataGo iOS"; + }; + name = Release; + }; + E18F3E382A51466C00D335E1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4L5BJK5M8K; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "ccy.KataGo-iOSUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "KataGo iOS"; + }; + name = Debug; + }; + E18F3E392A51466C00D335E1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4L5BJK5M8K; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "ccy.KataGo-iOSUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "KataGo iOS"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E11887E92B0830C900637D44 /* Build configuration list for PBXNativeTarget "KataGoSwift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E11887EA2B0830C900637D44 /* Debug */, + E11887EB2B0830C900637D44 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E118EE982B081C3300637D44 /* Build configuration list for PBXNativeTarget "katago" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E118EE992B081C3300637D44 /* Debug */, + E118EE9A2B081C3300637D44 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E18F3E082A51466A00D335E1 /* Build configuration list for PBXProject "KataGo iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E18F3E2F2A51466C00D335E1 /* Debug */, + E18F3E302A51466C00D335E1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E18F3E312A51466C00D335E1 /* Build configuration list for PBXNativeTarget "KataGo iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E18F3E322A51466C00D335E1 /* Debug */, + E18F3E332A51466C00D335E1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E18F3E342A51466C00D335E1 /* Build configuration list for PBXNativeTarget "KataGo iOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E18F3E352A51466C00D335E1 /* Debug */, + E18F3E362A51466C00D335E1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E18F3E372A51466C00D335E1 /* Build configuration list for PBXNativeTarget "KataGo iOSUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E18F3E382A51466C00D335E1 /* Debug */, + E18F3E392A51466C00D335E1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = E18F3E052A51466A00D335E1 /* Project object */; +} diff --git a/ios/KataGo iOS/KataGo iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/KataGo iOS/KataGo iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/KataGo iOS/KataGo iOS.xcodeproj/xcshareddata/xcschemes/KataGo iOS.xcscheme b/ios/KataGo iOS/KataGo iOS.xcodeproj/xcshareddata/xcschemes/KataGo iOS.xcscheme new file mode 100644 index 000000000..510b8f388 --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS.xcodeproj/xcshareddata/xcschemes/KataGo iOS.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/KataGo iOS/KataGo iOS/AnalysisView.swift b/ios/KataGo iOS/KataGo iOS/AnalysisView.swift new file mode 100644 index 000000000..1a905c237 --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/AnalysisView.swift @@ -0,0 +1,248 @@ +// +// AnalysisView.swift +// KataGo iOS +// +// Created by Chin-Chang Yang on 2023/9/7. +// + +import SwiftUI + +struct AnalysisView: View { + @EnvironmentObject var analysis: Analysis + @EnvironmentObject var board: ObservableBoard + let geometry: GeometryProxy + + var dimensions: Dimensions { + Dimensions(geometry: geometry, board: board) + } + + var shadows: some View { + ForEach(analysis.data, id: \.self) { data in + if let move = data["move"] { + if let point = moveToPoint(move: move) { + // Shadow + Circle() + .stroke(Color.black.opacity(0.5), lineWidth: dimensions.squareLength / 32) + .blur(radius: dimensions.squareLength / 32) + .frame(width: dimensions.squareLength, height: dimensions.squareLength) + .position(x: dimensions.marginWidth + CGFloat(point.x) * dimensions.squareLength, + y: dimensions.marginHeight + CGFloat(point.y) * dimensions.squareLength) + } + } + } + } + + func computeDefiniteness(_ whiteness: Double) -> Double { + return Swift.abs(whiteness - 0.5) * 2 + } + + var ownerships: some View { + let sortedOwnershipKeys = analysis.ownership.keys.sorted() + + return ForEach(sortedOwnershipKeys, id: \.self) { point in + if let ownership = analysis.ownership[point] { + let whiteness = (analysis.nextColorForAnalysis == .white) ? (Double(ownership.mean) + 1) / 2 : (Double(-ownership.mean) + 1) / 2 + let definiteness = computeDefiniteness(whiteness) + // Show a black or white square if definiteness is high and stdev is low + // Show nothing if definiteness is low and stdev is low + // Show a square with linear gradient of black and white if definiteness is low and stdev is high + let scale = max(CGFloat(definiteness), CGFloat(ownership.stdev ?? 0)) * 0.7 + + Rectangle() + .foregroundColor(Color(hue: 0, saturation: 0, brightness: whiteness).opacity(0.8)) + .frame(width: dimensions.squareLength * scale, height: dimensions.squareLength * scale) + .position(x: dimensions.marginWidth + CGFloat(point.x) * dimensions.squareLength, + y: dimensions.marginHeight + CGFloat(point.y) * dimensions.squareLength) + } + } + } + + var moves: some View { + let maxVisits = computeMaxVisits() + + return ForEach(analysis.data, id: \.self) { data in + if let move = data["move"] { + if let point = moveToPoint(move: move) { + let winrate = Float(data["winrate"] ?? "0") ?? 0 + let visits = Int(data["visits"] ?? "0") ?? 0 + let isHidden = Float(visits) < (0.1 * Float(maxVisits)) + let color = computeColorByVisits(isHidden: isHidden, visits: visits, maxVisits: maxVisits) + + ZStack { + Circle() + .foregroundColor(color) + if !isHidden { + VStack { + Text(String(format: "%2.0f%%", winrate * 100)) + .font(.system(size: 500)) + .minimumScaleFactor(0.01) + .bold() + + Text(convertToSIUnits(visits)) + .font(.system(size: 500)) + .minimumScaleFactor(0.01) + + if let scoreLead = data["scoreLead"] { + let text = String(format: "%+.1f", (Float(scoreLead) ?? 0)) + Text(text) + .font(.system(size: 500)) + .minimumScaleFactor(0.01) + } + } + } + } + .frame(width: dimensions.squareLength, height: dimensions.squareLength) + .position(x: dimensions.marginWidth + CGFloat(point.x) * dimensions.squareLength, + y: dimensions.marginHeight + CGFloat(point.y) * dimensions.squareLength) + } + } + } + } + + var body: some View { + shadows + ownerships + moves + } + + func convertToSIUnits(_ number: Int) -> String { + let prefixes: [(prefix: String, value: Int)] = [ + ("T", 1_000_000_000_000), // Tera + ("G", 1_000_000_000), // Giga + ("M", 1_000_000), // Mega + ("k", 1_000) // Kilo + ] + + var result = Double(number) + + for (prefix, threshold) in prefixes { + if number >= threshold { + result = Double(number) / Double(threshold) + return String(format: "%.1f%@", result, prefix) + } + } + + return "\(number)" + } + + func computeColorByWinrate(isHidden: Bool, winrate: Float, minWinrate: Float, maxWinrate: Float) -> Color { + let opacity = isHidden ? 0.1 : 0.5 + + if winrate == maxWinrate { + return .cyan.opacity(opacity) + } else { + let ratio = min(1, max(0.01, winrate - minWinrate) / max(0.01, maxWinrate - minWinrate)) + + let fraction = 2 / (pow((1 / ratio) - 1, 0.9) + 1) + + if fraction < 1 { + let hue = cbrt(fraction * fraction) / 2 + return Color(hue: Double(hue) / 2, saturation: 1, brightness: 1).opacity(opacity) + } else { + let hue = 1 - (sqrt(2 - fraction) / 2) + return Color(hue: Double(hue) / 2, saturation: 1, brightness: 1).opacity(opacity) + } + } + } + + func computeBaseColorByVisits(visits: Int, maxVisits: Int) -> Color { + if visits == maxVisits { + return Color(red: 0, green: 1, blue: 1) + } else { + let ratio = min(1, max(0.01, Float(visits)) / max(0.01, Float(maxVisits))) + + let fraction = 2 / (pow((1 / ratio) - 1, 0.9) + 1) + + if fraction < 1 { + let hue = cbrt(fraction * fraction) / 2 + return Color(hue: Double(hue) / 2, saturation: 1, brightness: 1) + } else { + let hue = 1 - (sqrt(2 - fraction) / 2) + return Color(hue: Double(hue) / 2, saturation: 1, brightness: 1) + } + } + } + + func computeColorByVisits(isHidden: Bool, visits: Int, maxVisits: Int) -> Color { + let baseColor = computeBaseColorByVisits(visits: visits, maxVisits: maxVisits) + let opacity = isHidden ? 0.2 : 0.8 + return baseColor.opacity(opacity) + } + + func computeMinMaxWinrate() -> (Float, Float) { + let winrates = analysis.data.map() { data in + Float(data["winrate"] ?? "0") ?? 0 + } + + let minWinrate = winrates.reduce(1) { + min($0, $1) + } + + let maxWinrate = winrates.reduce(0) { + max($0, $1) + } + + return (minWinrate, maxWinrate) + } + + func computeMaxVisits() -> Int { + let allVisits = analysis.data.map() { data in + Int(data["visits"] ?? "0") ?? 0 + } + + let maxVisits = allVisits.reduce(0) { + max($0, $1) + } + + return maxVisits + } + + func moveToPoint(move: String) -> BoardPoint? { + // Mapping letters A-AD (without I) to numbers 0-28 + let letterMap: [String: Int] = [ + "A": 0, "B": 1, "C": 2, "D": 3, "E": 4, + "F": 5, "G": 6, "H": 7, "J": 8, "K": 9, + "L": 10, "M": 11, "N": 12, "O": 13, "P": 14, + "Q": 15, "R": 16, "S": 17, "T": 18, "U": 19, + "V": 20, "W": 21, "X": 22, "Y": 23, "Z": 24, + "AA": 25, "AB": 26, "AC": 27, "AD": 28 + ] + + let pattern = /([^\d\W]+)(\d+)/ + if let match = move.firstMatch(of: pattern) { + if let x = letterMap[String(match.1).uppercased()], + let y = Int(match.2) { + // Subtract 1 from y to make it 0-indexed + return BoardPoint(x: x, y: y - 1) + } else { + return nil + } + } else { + return nil + } + } +} + +struct AnalysisView_Previews: PreviewProvider { + static let analysis = Analysis() + static let board = ObservableBoard() + + static var previews: some View { + ZStack { + Rectangle() + .foregroundColor(.brown) + + GeometryReader { geometry in + AnalysisView(geometry: geometry) + } + .environmentObject(analysis) + .environmentObject(board) + .onAppear() { + AnalysisView_Previews.board.width = 2 + AnalysisView_Previews.board.height = 2 + AnalysisView_Previews.analysis.data = [["move": "A1", "winrate": "0.54321012345", "scoreLead": "0.123456789", "order": "0", "visits": "12345678"], ["move": "B1", "winrate": "0.4", "scoreLead": "-9.8", "order": "1", "visits": "2345678"], ["move": "A2", "winrate": "0.321", "scoreLead": "-12.345", "order": "2", "visits": "198"]] + AnalysisView_Previews.analysis.ownership = [BoardPoint(x: 0, y: 0): Ownership(mean: 0.12, stdev: 0.5), BoardPoint(x: 1, y: 0): Ownership(mean: 0.987654321, stdev: 0.1), BoardPoint(x: 0, y: 1): Ownership(mean: -0.123456789, stdev: 0.4), BoardPoint(x: 1, y: 1): Ownership(mean: -0.98, stdev: 0.2)] + } + } + } +} diff --git a/ios/KataGo iOS/KataGo iOS/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/KataGo iOS/KataGo iOS/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/KataGo iOS/KataGo iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/KataGo iOS/KataGo iOS/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..13613e3ee --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/KataGo iOS/KataGo iOS/Assets.xcassets/Contents.json b/ios/KataGo iOS/KataGo iOS/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/KataGo iOS/KataGo iOS/BoardLineView.swift b/ios/KataGo iOS/KataGo iOS/BoardLineView.swift new file mode 100644 index 000000000..73e10144b --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/BoardLineView.swift @@ -0,0 +1,102 @@ +// +// BoardLineView.swift +// KataGo iOS +// +// Created by Chin-Chang Yang on 2023/9/9. +// + +import SwiftUI + +struct BoardLineView: View { + let dimensions: Dimensions + let texture = WoodImage.createTexture() + let boardWidth: CGFloat + let boardHeight: CGFloat + + var body: some View { + ZStack { + drawBoardBackground(texture: texture, dimensions: dimensions) + drawLines(dimensions: dimensions) + drawStarPoints(dimensions: dimensions) + } + } + + private func drawBoardBackground(texture: UIImage, dimensions: Dimensions) -> some View { + Group { + Image(uiImage: texture) + .resizable() + .frame(width: (dimensions.boardWidth + dimensions.squareLength / 2), height: dimensions.boardHeight + (dimensions.squareLength / 2)) + } + } + + private func drawLines(dimensions: Dimensions) -> some View { + Group { + ForEach(0.. some View { + Path { path in + path.move(to: CGPoint(x: dimensions.marginWidth, y: dimensions.marginHeight + CGFloat(i) * dimensions.squareLength)) + path.addLine(to: CGPoint(x: dimensions.marginWidth + dimensions.boardWidth - dimensions.squareLength, y: dimensions.marginHeight + CGFloat(i) * dimensions.squareLength)) + } + .stroke(Color.black) + } + + private func verticalLine(i: Int, dimensions: Dimensions) -> some View { + Path { path in + path.move(to: CGPoint(x: dimensions.marginWidth + CGFloat(i) * dimensions.squareLength, y: dimensions.marginHeight)) + path.addLine(to: CGPoint(x: dimensions.marginWidth + CGFloat(i) * dimensions.squareLength, y: dimensions.marginHeight + dimensions.boardHeight - dimensions.squareLength)) + } + .stroke(Color.black) + } + + private func drawStarPoint(x: Int, y: Int, dimensions: Dimensions) -> some View { + // Big black dot + Circle() + .frame(width: dimensions.squareLength / 4, height: dimensions.squareLength / 4) + .foregroundColor(Color.black) + .position(x: dimensions.marginWidth + CGFloat(x) * dimensions.squareLength, + y: dimensions.marginHeight + CGFloat(y) * dimensions.squareLength) + } + + private func drawStarPointsForSize(points: [BoardPoint], dimensions: Dimensions) -> some View { + ForEach(points, id: \.self) { point in + drawStarPoint(x: point.x, y: point.y, dimensions: dimensions) + } + } + + private func drawStarPoints(dimensions: Dimensions) -> some View { + Group { + if boardWidth == 19 && boardHeight == 19 { + // Draw star points for 19x19 board + drawStarPointsForSize(points: [BoardPoint(x: 3, y: 3), BoardPoint(x: 3, y: 9), BoardPoint(x: 3, y: 15), BoardPoint(x: 9, y: 3), BoardPoint(x: 9, y: 9), BoardPoint(x: 9, y: 15), BoardPoint(x: 15, y: 3), BoardPoint(x: 15, y: 9), BoardPoint(x: 15, y: 15)], dimensions: dimensions) + } else if boardWidth == 13 && boardHeight == 13 { + // Draw star points for 13x13 board + drawStarPointsForSize(points: [BoardPoint(x: 6, y: 6), BoardPoint(x: 3, y: 3), BoardPoint(x: 3, y: 9), BoardPoint(x: 9, y: 3), BoardPoint(x: 9, y: 9)], dimensions: dimensions) + } else if boardWidth == 9 && boardHeight == 9 { + // Draw star points for 9x9 board + drawStarPointsForSize(points: [BoardPoint(x: 4, y: 4), BoardPoint(x: 2, y: 2), BoardPoint(x: 2, y: 6), BoardPoint(x: 6, y: 2), BoardPoint(x: 6, y: 6)], dimensions: dimensions) + } + } + } +} + +struct BoardLineView_Previews: PreviewProvider { + static let board = ObservableBoard() + static var previews: some View { + GeometryReader { geometry in + let dimensions = Dimensions(geometry: geometry, board: board) + BoardLineView(dimensions: dimensions, boardWidth: board.width, boardHeight: board.height) + } + .onAppear() { + BoardLineView_Previews.board.width = 13 + BoardLineView_Previews.board.height = 13 + } + } +} diff --git a/ios/KataGo iOS/KataGo iOS/ButtonView.swift b/ios/KataGo iOS/KataGo iOS/ButtonView.swift new file mode 100644 index 000000000..2384683cb --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/ButtonView.swift @@ -0,0 +1,36 @@ +// +// ButtonView.swift +// KataGo iOS +// +// Created by Chin-Chang Yang on 2023/9/5. +// + +import SwiftUI + +struct ButtonView: View { + @EnvironmentObject var messagesObject: MessagesObject + @EnvironmentObject var config: Config + let commands: [String] + + var body: some View { + HStack { + ForEach(commands, id:\.self) { command in + CommandButton(title: command) { + messagesObject.messages.append(Message(text: command, maxLength: config.maxMessageCharacters)) + KataGoHelper.sendCommand(command) + } + .scaledToFit() + } + } + } +} + +struct ButtonView_Previews: PreviewProvider { + static let commands = ["kata-set-rules chinese", "komi 7"] + static var messagesObject = MessagesObject() + + static var previews: some View { + ButtonView(commands: commands) + .environmentObject(messagesObject) + } +} diff --git a/ios/KataGo iOS/KataGo iOS/CommandView.swift b/ios/KataGo iOS/KataGo iOS/CommandView.swift new file mode 100644 index 000000000..b69cd89c5 --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/CommandView.swift @@ -0,0 +1,90 @@ +// +// CommandView.swift +// KataGo iOS +// +// Created by Chin-Chang Yang on 2023/9/2. +// + +import SwiftUI + +struct CommandButton: View { + var title: String + var action: () -> Void + + var body: some View { + Button(action: action) { + Text(title) + .foregroundColor(.white) + .padding() + .background(Color.blue) + .clipShape(RoundedRectangle(cornerRadius: 50)) + .font(.body.monospaced()) + } + } +} + +struct CommandView: View { + @EnvironmentObject var messagesObject: MessagesObject + @EnvironmentObject var stones: Stones + @EnvironmentObject var config: Config + @State private var command = "" + + var body: some View { + VStack { + ScrollViewReader { scrollView in + ScrollView(.vertical) { + // Vertically show each KataGo message + LazyVStack { + ForEach(messagesObject.messages) { message in + Text(message.text) + .font(.body.monospaced()) + .id(message.id) + .textSelection(.enabled) + .frame(maxWidth: .infinity, alignment: .leading) + } + } + .onChange(of: messagesObject.messages) { value in + // Scroll to the last message + scrollView.scrollTo(value.last?.id) + } + } + } + + HStack { + TextField("Enter your GTP command (list_commands)", text: $command) + .disableAutocorrection(true) + .textInputAutocapitalization(.never) + .onSubmit { + messagesObject.messages.append(Message(text: command, maxLength: config.maxMessageCharacters)) + KataGoHelper.sendCommand(command) + command = "" + } + Button(action: { + messagesObject.messages.append(Message(text: command, maxLength: config.maxMessageCharacters)) + KataGoHelper.sendCommand(command) + command = "" + }) { + Image(systemName: "return") + } + } + .padding() + + ButtonView(commands: ["kata-set-rules chinese", "komi 7"]) + } + .padding() + .onAppear() { + KataGoHelper.sendCommand("stop") + } + } +} + +struct CommandView_Previews: PreviewProvider { + static let messageObject = MessagesObject() + static let config = Config() + + static var previews: some View { + CommandView() + .environmentObject(messageObject) + .environmentObject(config) + } +} diff --git a/ios/KataGo iOS/KataGo iOS/ConfigView.swift b/ios/KataGo iOS/KataGo iOS/ConfigView.swift new file mode 100644 index 000000000..db8d0053f --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/ConfigView.swift @@ -0,0 +1,112 @@ +// +// ConfigView.swift +// KataGo iOS +// +// Created by Chin-Chang Yang on 2023/9/19. +// + +import SwiftUI + +struct EditButtonBar: View { + var body: some View { + HStack { + Spacer() + EditButton() + } + } +} + +struct ConfigItem: View { + @Environment(\.editMode) private var editMode + let title: String + @Binding var content: String + + var body: some View { + HStack { + Text(title) + Spacer() + if editMode?.wrappedValue.isEditing == true { + TextField("", text: $content) + .multilineTextAlignment(.trailing) + .background(Color(white: 0.9)) + } else { + Text(content) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + } +} + +struct ConfigItems: View { + @EnvironmentObject var config: Config + @State var isAnalyzing = Config.defaultIsAnalyzing + @State var maxMessageCharacters: String = "\(Config.defaultMaxMessageCharacters)" + @State var maxAnalysisMoves: String = "\(Config.defaultMaxAnalysisMoves)" + @State var analysisInterval: String = "\(Config.defaultAnalysisInterval)" + @State var maxMessageLines: String = "\(Config.defaultMaxMessageLines)" + + var body: some View { + VStack { + HStack { + Toggle(isOn: $isAnalyzing) { + Text("Analysis") + } + .onChange(of: isAnalyzing) { newFlag in + config.isAnalyzing = newFlag + } + } + .padding(.bottom) + + ConfigItem(title: "Max message characters:", content: $maxMessageCharacters) + .onChange(of: maxMessageCharacters) { newText in + config.maxMessageCharacters = Int(newText) ?? + Config.defaultMaxMessageCharacters + } + .padding(.bottom) + + ConfigItem(title: "Max analysis moves:", content: $maxAnalysisMoves) + .onChange(of: maxAnalysisMoves) { newText in + config.maxAnalysisMoves = Int(newText) ?? + Config.defaultMaxAnalysisMoves + } + .padding(.bottom) + + ConfigItem(title: "Analysis interval (centiseconds):", content: $analysisInterval) + .onChange(of: analysisInterval) { newText in + config.analysisInterval = Int(newText) ?? + Config.defaultAnalysisInterval + } + .padding(.bottom) + + ConfigItem(title: "Max message lines:", content: $maxMessageLines) + .onChange(of: maxMessageLines) { newText in + config.maxMessageLines = Int(newText) ?? + Config.defaultMaxMessageLines + } + } + } +} + +struct ConfigView: View { + var body: some View { + VStack { + EditButtonBar() + .padding() + ConfigItems() + .padding() + } + .frame(maxHeight: .infinity, alignment: .topLeading) + .onAppear() { + KataGoHelper.sendCommand("stop") + } + } +} + +struct ConfigView_Previews: PreviewProvider { + static let isEditing = EditMode.inactive + static let config = Config() + static var previews: some View { + ConfigView() + .environmentObject(config) + } +} diff --git a/ios/KataGo iOS/KataGo iOS/ContentView.swift b/ios/KataGo iOS/KataGo iOS/ContentView.swift new file mode 100644 index 000000000..cd0b82adb --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/ContentView.swift @@ -0,0 +1,220 @@ +// +// ContentView.swift +// KataGo iOS +// +// Created by Chin-Chang Yang on 2023/7/2. +// + +import SwiftUI + +struct ContentView: View { + @StateObject var stones = Stones() + @StateObject var messagesObject = MessagesObject() + @StateObject var board = ObservableBoard() + @StateObject var player = PlayerObject() + @StateObject var analysis = Analysis() + @StateObject var config = Config() + @State private var isShowingBoard = false + @State private var boardText: [String] = [] + @State var isEditing = EditMode.inactive + + init() { + // Start a thread to run KataGo GTP + Thread { + KataGoHelper.runGtp() + }.start() + } + + var body: some View { + TabView() { + CommandView() + .tabItem { + Label("Command", systemImage: "text.alignleft") + } + + GobanView() + .tabItem { + Label("Goban", systemImage: "circle") + } + + ConfigView() + .tabItem { + Label("Config", systemImage: "slider.horizontal.3") + } + } + .environmentObject(stones) + .environmentObject(messagesObject) + .environmentObject(board) + .environmentObject(player) + .environmentObject(analysis) + .environmentObject(config) + .environment(\.editMode, $isEditing) + .onAppear() { + // Get messages from KataGo and append to the list of messages + createMessageTask() + } + } + + /// Create message task + private func createMessageTask() { + Task { + messagesObject.messages.append(Message(text: "Initializing...", maxLength: config.maxMessageCharacters)) + KataGoHelper.sendCommand("showboard") + while true { + let line = await Task.detached { + // Get a message line from KataGo + return KataGoHelper.getMessageLine() + }.value + + // Create a message with the line + let message = Message(text: line, maxLength: config.maxMessageCharacters) + + // Append the message to the list of messages + messagesObject.messages.append(message) + + // Collect board information + maybeCollectBoard(message: line) + + // Collect analysis information + maybeCollectAnalysis(message: line) + + // Remove when there are too many messages + while messagesObject.messages.count > config.maxMessageLines { + messagesObject.messages.removeFirst() + } + } + } + } + + func maybeCollectBoard(message: String) { + if isShowingBoard { + if message.prefix("Next player".count) == "Next player" { + isShowingBoard = false + (stones.blackPoints, stones.whitePoints, board.width, board.height) = parseBoardPoints(board: boardText) + if message.prefix("Next player: Black".count) == "Next player: Black" { + player.nextColorForPlayCommand = .black + player.nextColorFromShowBoard = .black + } else { + player.nextColorForPlayCommand = .white + player.nextColorFromShowBoard = .white + } + } else { + boardText.append(message) + } + } else { + if message.prefix("= MoveNum".count) == "= MoveNum" { + boardText = [] + isShowingBoard = true + } + } + } + + func parseBoardPoints(board: [String]) -> ([BoardPoint], [BoardPoint], CGFloat, CGFloat) { + var blackStones: [BoardPoint] = [] + var whiteStones: [BoardPoint] = [] + + let height = CGFloat(board.count - 1) // Subtracting 1 to exclude the header + let width = CGFloat((board.last?.dropFirst(2).count ?? 0) / 2) // Drop the first 2 characters for the y-coordinate and divide by 2 because of spaces between cells + + // Start from index 1 to skip the header line + for (lineIndex, line) in board.enumerated() where lineIndex > 0 { + // Get y-coordinate from the beginning of the line, and subtract 1 to start from 0 + let y = (Int(line.prefix(2).trimmingCharacters(in: .whitespaces)) ?? 1) - 1 + + // Start parsing after the space that follows the y-coordinate + for (charIndex, char) in line.dropFirst(3).enumerated() where char == "X" || char == "O" { + let xCoord = charIndex / 2 + if char == "X" { + blackStones.append(BoardPoint(x: xCoord, y: y)) + } else if char == "O" { + whiteStones.append(BoardPoint(x: xCoord, y: y)) + } + } + } + + return (blackStones, whiteStones, width, height) + } + + func maybeCollectAnalysis(message: String) { + if message.starts(with: /info/) { + let splitData = message.split(separator: "info") + analysis.data = splitData.map { + extractMoveData(dataLine: String($0)) + } + + if let lastData = splitData.last { + analysis.ownership = extractOwnership(message: String(lastData)) + } + + analysis.nextColorForAnalysis = player.nextColorFromShowBoard + } + } + + func extractMoveData(dataLine: String) -> [String: String] { + // Define patterns for extracting relevant information + let patterns: [String: Regex] = [ + "move": /move (\w+\d+)/, + "visits": /visits (\d+)/, + "winrate": /winrate ([\d.eE]+)/, + "scoreLead": /scoreLead ([-\d.eE]+)/ + ] + + var moveData: [String: String] = [:] + for (key, pattern) in patterns { + if let match = dataLine.firstMatch(of: pattern) { + moveData[key] = String(match.1) + } + } + + return moveData + } + + func extractOwnershipMean(message: String) -> [Float] { + let pattern = /ownership ([-\d\s.eE]+)/ + if let match = message.firstMatch(of: pattern) { + let mean = match.1.split(separator: " ").compactMap { Float($0) + } + assert(mean.count == Int(board.width * board.height)) + return mean + } + + return [] + } + + func extractOwnershipStdev(message: String) -> [Float] { + let pattern = /ownershipStdev ([-\d\s.eE]+)/ + if let match = message.firstMatch(of: pattern) { + let stdev = match.1.split(separator: " ").compactMap { Float($0) + } + assert(stdev.count == Int(board.width * board.height)) + return stdev + } + + return [] + } + + func extractOwnership(message: String) -> [BoardPoint: Ownership] { + let mean = extractOwnershipMean(message: message) + let stdev = extractOwnershipStdev(message: message) + if !mean.isEmpty && !stdev.isEmpty { + var dictionary: [BoardPoint: Ownership] = [:] + var i = 0 + for y in stride(from:Int(board.height - 1), through: 0, by: -1) { + for x in 0.. String? { + let calculateCoordinate = { (point: CGFloat, margin: CGFloat, length: CGFloat) -> Int in + return Int(round((point - margin) / length)) + } + + let y = calculateCoordinate(location.y, dimensions.marginHeight, dimensions.squareLength) + 1 + let x = calculateCoordinate(location.x, dimensions.marginWidth, dimensions.squareLength) + + guard (1...Int(board.height)).contains(y), (0.. + +@interface KataGoHelper : NSObject + ++ (void)runGtp; + ++ (NSString * _Nonnull)getMessageLine; + ++ (void)sendCommand:(NSString * _Nonnull)command; + +@end + +#endif /* KataGoHelper_h */ diff --git a/ios/KataGo iOS/KataGo iOS/KataGoHelper.mm b/ios/KataGo iOS/KataGo iOS/KataGoHelper.mm new file mode 100644 index 000000000..83e4fb1ba --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/KataGoHelper.mm @@ -0,0 +1,128 @@ +// +// KataGoHelper.m +// KataGo iOS +// +// Created by Chin-Chang Yang on 2023/7/2. +// + +#import "KataGoHelper.h" +#import "../../cpp/main.h" +#import +#import "../../cpp/neuralnet/coremlbackend.h" + +using namespace std; + +// Thread-safe stream buffer +class ThreadSafeStreamBuf : public std::streambuf { + std::string buffer; + std::mutex m; + std::condition_variable cv; + std::atomic done {false}; + +public: + int overflow(int c) override { + std::lock_guard lock(m); + buffer += static_cast(c); + if (c == '\n') { + cv.notify_all(); + } + return c; + } + + int underflow() override { + std::unique_lock lock(m); + cv.wait(lock, [&]{ return !buffer.empty() || done; }); + if (buffer.empty()) { + return std::char_traits::eof(); + } + return buffer.front(); + } + + int uflow() override { + std::unique_lock lock(m); + cv.wait(lock, [&]{ return !buffer.empty() || done; }); + if (buffer.empty()) { + return std::char_traits::eof(); + } + int c = buffer.front(); + buffer.erase(buffer.begin()); + return c; + } + + void setDone() { + done = true; + cv.notify_all(); + } +}; + +// Thread-safe stream buffer from KataGo +ThreadSafeStreamBuf tsbFromKataGo; + +// Input stream from KataGo +istream inFromKataGo(&tsbFromKataGo); + +// Thread-safe stream buffer to KataGo +ThreadSafeStreamBuf tsbToKataGo; + +// Output stream to KataGo +ostream outToKataGo(&tsbToKataGo); + +@implementation KataGoHelper + +/// Run KataGo main command GTP with default model and config ++ (void)runGtp { + NSBundle* mainBundle = [NSBundle mainBundle]; + + // Get the default model path + NSString* modelPath = [mainBundle pathForResource:@"default_model" + ofType:@"bin.gz"]; + + // Get the default config path + NSString* configPath = [mainBundle pathForResource:@"default_gtp" + ofType:@"cfg"]; + + // Replace the global cout object with the custom one + cout.rdbuf(&tsbFromKataGo); + + // Replace the global cin object with the custom one + cin.rdbuf(&tsbToKataGo); + + vector subArgs; +#if true + // Call the main command gtp + subArgs.push_back(string("gtp")); + subArgs.push_back(string("-model")); + subArgs.push_back(string([modelPath UTF8String])); + subArgs.push_back(string("-config")); + subArgs.push_back(string([configPath UTF8String])); + MainCmds::gtp(subArgs); +#else + // Call the main command benchmark + subArgs.push_back(string("benchmark")); + subArgs.push_back(string("-model")); + subArgs.push_back(string([modelPath UTF8String])); + subArgs.push_back(string("-config")); + subArgs.push_back(string([configPath UTF8String])); + subArgs.push_back(string("-t")); + subArgs.push_back(string("2,4,8")); + MainCmds::benchmark(subArgs); +#endif +} + ++ (NSString * _Nonnull)getMessageLine { + // Get a line from the input stream from KataGo + string cppLine; + getline(inFromKataGo, cppLine); + + // Convert the C++ std:string into an NSString + NSString* messageLine = [NSString stringWithUTF8String:cppLine.c_str()]; + + return messageLine; +} + ++ (void)sendCommand:(NSString * _Nonnull)command { + // Write GTP commands to the outToKataGo + outToKataGo << string([command UTF8String]) << endl; +} + +@end diff --git a/ios/KataGo iOS/KataGo iOS/KataGoModel.swift b/ios/KataGo iOS/KataGo iOS/KataGoModel.swift new file mode 100644 index 000000000..a3a26f140 --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/KataGoModel.swift @@ -0,0 +1,128 @@ +// +// KataGoModel.swift +// KataGo iOS +// +// Created by Chin-Chang Yang on 2023/10/1. +// + +import SwiftUI + +class ObservableBoard: ObservableObject { + @Published var width: CGFloat = 19 + @Published var height: CGFloat = 19 +} + +struct BoardPoint: Hashable, Comparable { + let x: Int + let y: Int + + static func < (lhs: BoardPoint, rhs: BoardPoint) -> Bool { + return (lhs.y, lhs.x) < (rhs.y, rhs.x) + } +} + +class Stones: ObservableObject { + @Published var blackPoints: [BoardPoint] = [] + @Published var whitePoints: [BoardPoint] = [] +} + +enum PlayerColor { + case black + case white +} + +class PlayerObject: ObservableObject { + @Published var nextColorForPlayCommand = PlayerColor.black + @Published var nextColorFromShowBoard = PlayerColor.black +} + +struct Ownership { + let mean: Float + let stdev: Float? + + init(mean: Float, stdev: Float?) { + self.mean = mean + self.stdev = stdev + } +} + +class Analysis: ObservableObject { + @Published var nextColorForAnalysis = PlayerColor.white + @Published var data: [[String: String]] = [] + @Published var ownership: [BoardPoint: Ownership] = [:] +} + +class Config: ObservableObject { + @Published var isAnalyzing: Bool = defaultIsAnalyzing + @Published var maxMessageCharacters: Int = defaultMaxMessageCharacters + @Published var maxAnalysisMoves: Int = defaultMaxAnalysisMoves + @Published var analysisInterval: Int = defaultAnalysisInterval + @Published var maxMessageLines: Int = defaultMaxMessageLines + + func getKataAnalyzeCommand() -> String { + return "kata-analyze interval \(analysisInterval) maxmoves \(maxAnalysisMoves) ownership true ownershipStdev true" + } +} + +extension Config { + static let defaultIsAnalyzing = true + static let defaultMaxMessageCharacters = 200 + static let defaultMaxAnalysisMoves = 8 + static let defaultAnalysisInterval = 20 + static let defaultMaxMessageLines = 100 +} + +struct Dimensions { + let squareLength: CGFloat + let squareLengthDiv2: CGFloat + let squareLengthDiv4: CGFloat + let squareLengthDiv8: CGFloat + let squareLengthDiv16: CGFloat + let boardWidth: CGFloat + let boardHeight: CGFloat + let marginWidth: CGFloat + let marginHeight: CGFloat + let stoneLength: CGFloat + + init(geometry: GeometryProxy, board: ObservableBoard) { + self.init(geometry: geometry, width: board.width, height: board.height) + } + + private init(geometry: GeometryProxy, width: CGFloat, height: CGFloat) { + let totalWidth = geometry.size.width + let totalHeight = geometry.size.height + let squareWidth = totalWidth / (width + 1) + let squareHeight = totalHeight / (height + 1) + squareLength = min(squareWidth, squareHeight) + squareLengthDiv2 = squareLength / 2 + squareLengthDiv4 = squareLength / 4 + squareLengthDiv8 = squareLength / 8 + squareLengthDiv16 = squareLength / 16 + boardWidth = width * squareLength + boardHeight = height * squareLength + marginWidth = (totalWidth - boardWidth + squareLength) / 2 + marginHeight = (totalHeight - boardHeight + squareLength) / 2 + stoneLength = squareLength * 0.95 + } +} + +/// Message with a text and an ID +struct Message: Identifiable, Equatable, Hashable { + /// Identification of this message + let id = UUID() + + /// Text of this message + let text: String + + /// Initialize a message with a text and a max length + /// - Parameters: + /// - text: a text + /// - maxLength: a max length + init(text: String, maxLength: Int) { + self.text = String(text.prefix(maxLength)) + } +} + +class MessagesObject: ObservableObject { + @Published var messages: [Message] = [] +} diff --git a/ios/KataGo iOS/KataGo iOS/KataGo_iOSApp.swift b/ios/KataGo iOS/KataGo iOS/KataGo_iOSApp.swift new file mode 100644 index 000000000..cfd878f14 --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/KataGo_iOSApp.swift @@ -0,0 +1,17 @@ +// +// KataGo_iOSApp.swift +// KataGo iOS +// +// Created by Chin-Chang Yang on 2023/7/2. +// + +import SwiftUI + +@main +struct KataGo_iOSApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/ios/KataGo iOS/KataGo iOS/Preview Content/Preview Assets.xcassets/Contents.json b/ios/KataGo iOS/KataGo iOS/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/KataGo iOS/KataGo iOS/StoneView.swift b/ios/KataGo iOS/KataGo iOS/StoneView.swift new file mode 100644 index 000000000..7d5f20304 --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/StoneView.swift @@ -0,0 +1,152 @@ +// +// StoneView.swift +// KataGo iOS +// +// Created by Chin-Chang Yang on 2023/9/6. +// + +import SwiftUI + +struct StoneView: View { + @EnvironmentObject var stones: Stones + @EnvironmentObject var board: ObservableBoard + let geometry: GeometryProxy + + var body: some View { + let dimensions = Dimensions(geometry: geometry, board: board) + drawStones(dimensions: dimensions) + } + + private func drawStoneBase(stoneColor: Color, x: Int, y: Int, dimensions: Dimensions) -> some View { + Circle() + .foregroundColor(stoneColor) + .frame(width: dimensions.stoneLength, height: dimensions.stoneLength) + .position(x: dimensions.marginWidth + CGFloat(x) * dimensions.squareLength, + y: dimensions.marginHeight + CGFloat(y) * dimensions.squareLength) + } + + private func drawLightEffect(stoneColor: Color, x: Int, y: Int, dimensions: Dimensions) -> some View { + Circle() + .fill(RadialGradient(gradient: Gradient(colors: [stoneColor, Color.white, Color.white]), center: .center, startRadius: dimensions.squareLengthDiv4, endRadius: 0)) + .offset(x: -dimensions.squareLengthDiv8, y: -dimensions.squareLengthDiv8) + .padding(dimensions.squareLengthDiv4) + .frame(width: dimensions.stoneLength, height: dimensions.stoneLength) + .position(x: dimensions.marginWidth + CGFloat(x) * dimensions.squareLength, + y: dimensions.marginHeight + CGFloat(y) * dimensions.squareLength) + .overlay { + // Mask some light + Circle() + .foregroundColor(stoneColor) + .blur(radius: dimensions.squareLengthDiv16) + .frame(width: dimensions.squareLengthDiv2, height: dimensions.squareLengthDiv2) + .position(x: dimensions.marginWidth + CGFloat(x) * dimensions.squareLength, + y: dimensions.marginHeight + CGFloat(y) * dimensions.squareLength) + } + } + + private func drawBlackStone(x: Int, y: Int, dimensions: Dimensions) -> some View { + + ZStack { + // Black stone + drawStoneBase(stoneColor: .black, x: x, y: y, dimensions: dimensions) + + // Light source effect + drawLightEffect(stoneColor: .black, x: x, y: y, dimensions: dimensions) + } + } + + private func drawBlackStones(dimensions: Dimensions) -> some View { + Group { + ForEach(stones.blackPoints, id: \.self) { point in + drawBlackStone(x: point.x, y: point.y, dimensions: dimensions) + } + } + } + + private func drawWhiteStone(x: Int, y: Int, dimensions: Dimensions) -> some View { + + ZStack { + // Make a white stone darker than light + let stoneColor = Color(white: 0.9) + + // White stone + drawStoneBase(stoneColor: stoneColor, x: x, y: y, dimensions: dimensions) + + // Light source effect + drawLightEffect(stoneColor: stoneColor, x: x, y: y, dimensions: dimensions) + } + } + + private func drawWhiteStones(dimensions: Dimensions) -> some View { + Group { + ForEach(stones.whitePoints, id: \.self) { point in + drawWhiteStone(x: point.x, y: point.y, dimensions: dimensions) + } + } + } + + private func drawShadow(x: Int, y: Int, dimensions: Dimensions) -> some View { + Group { + // Shifted shadow + Circle() + .shadow(radius: dimensions.squareLengthDiv16, x: dimensions.squareLengthDiv8, y: dimensions.squareLengthDiv8) + .frame(width: dimensions.stoneLength, height: dimensions.stoneLength) + .position(x: dimensions.marginWidth + CGFloat(x) * dimensions.squareLength, + y: dimensions.marginHeight + CGFloat(y) * dimensions.squareLength) + + // Centered shadow + Circle() + .stroke(Color.black.opacity(0.5), lineWidth: dimensions.squareLengthDiv16) + .blur(radius: dimensions.squareLengthDiv16) + .frame(width: dimensions.stoneLength, height: dimensions.stoneLength) + .position(x: dimensions.marginWidth + CGFloat(x) * dimensions.squareLength, + y: dimensions.marginHeight + CGFloat(y) * dimensions.squareLength) + } + } + + private func drawShadows(dimensions: Dimensions) -> some View { + Group { + ForEach(stones.blackPoints, id: \.self) { point in + drawShadow(x: point.x, y: point.y, dimensions: dimensions) + } + + ForEach(stones.whitePoints, id: \.self) { point in + drawShadow(x: point.x, y: point.y, dimensions: dimensions) + } + } + } + + private func drawStones(dimensions: Dimensions) -> some View { + ZStack { + drawShadows(dimensions: dimensions) + + Group { + drawBlackStones(dimensions: dimensions) + drawWhiteStones(dimensions: dimensions) + } + } + } +} + +struct StoneView_Previews: PreviewProvider { + static let stones = Stones() + static let board = ObservableBoard() + static var previews: some View { + ZStack { + Rectangle() + .foregroundColor(.brown) + + GeometryReader { geometry in + StoneView(geometry: geometry) + } + .environmentObject(stones) + .environmentObject(board) + .onAppear() { + StoneView_Previews.board.width = 2 + StoneView_Previews.board.height = 2 + StoneView_Previews.stones.blackPoints = [BoardPoint(x: 0, y: 0), BoardPoint(x: 1, y: 1)] + StoneView_Previews.stones.whitePoints = [BoardPoint(x: 0, y: 1), BoardPoint(x: 1, y: 0)] + } + } + } +} diff --git a/ios/KataGo iOS/KataGo iOS/ToolbarView.swift b/ios/KataGo iOS/KataGo iOS/ToolbarView.swift new file mode 100644 index 000000000..7ae02d340 --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/ToolbarView.swift @@ -0,0 +1,99 @@ +// +// ToolbarView.swift +// KataGo iOS +// +// Created by Chin-Chang Yang on 2023/10/1. +// + +import SwiftUI + +struct ToolbarItems: View { + @EnvironmentObject var player: PlayerObject + @EnvironmentObject var config: Config + + var body: some View { + Group { + Button(action: { + KataGoHelper.sendCommand("undo") + KataGoHelper.sendCommand("showboard") + if config.isAnalyzing { + KataGoHelper.sendCommand(config.getKataAnalyzeCommand()) + } + }) { + Image(systemName: "arrow.uturn.backward") + } + .padding() + + Button(action: { + let nextColor = (player.nextColorForPlayCommand == .black) ? "b" : "w" + let pass = "play \(nextColor) pass" + KataGoHelper.sendCommand(pass) + KataGoHelper.sendCommand("showboard") + if config.isAnalyzing { + KataGoHelper.sendCommand(config.getKataAnalyzeCommand()) + } + }) { + Image(systemName: "hand.raised") + } + .padding() + + Button(action: { + if config.isAnalyzing { + KataGoHelper.sendCommand(config.getKataAnalyzeCommand()) + } + }) { + Image(systemName: "play") + } + .padding() + + Button(action: { + if config.isAnalyzing { + KataGoHelper.sendCommand("stop") + } + }) { + Image(systemName: "stop") + } + .padding() + + Button(action: { + KataGoHelper.sendCommand("clear_board") + KataGoHelper.sendCommand("showboard") + if config.isAnalyzing { + KataGoHelper.sendCommand(config.getKataAnalyzeCommand()) + } + }) { + Image(systemName: "clear") + } + .padding() + } + } +} + +struct ToolbarView: View { + @Environment(\.horizontalSizeClass) var hSizeClass + @Environment(\.verticalSizeClass) var vSizeClass + + var body: some View { + if hSizeClass == .compact && vSizeClass == .regular { + HStack { + ToolbarItems() + } + } else { + VStack { + ToolbarItems() + } + } + } +} + +struct ToolbarView_Previews: PreviewProvider { + static let player = PlayerObject() + static let config = Config() + + static var previews: some View { + @State var isAnalyzing = true + ToolbarView() + .environmentObject(player) + .environmentObject(config) + } +} diff --git a/ios/KataGo iOS/KataGo iOS/WoodView.swift b/ios/KataGo iOS/KataGo iOS/WoodView.swift new file mode 100644 index 000000000..83d095918 --- /dev/null +++ b/ios/KataGo iOS/KataGo iOS/WoodView.swift @@ -0,0 +1,35 @@ +// +// WoodView.swift +// KataGo iOS +// +// Created by Chin-Chang Yang on 2023/9/2. +// + +import SwiftUI + +struct WoodImage { + static func createTexture() -> UIImage { + #if true + let textureString = "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" + #else + let textureString = "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" + #endif + + let imageData = Data(base64Encoded: textureString) + return UIImage(data: imageData!)! + } +} + +struct WoodView: View { + let texture = WoodImage.createTexture() + + var body: some View { + Image(uiImage: texture) + } +} + +struct WoodView_Previews: PreviewProvider { + static var previews: some View { + WoodView() + } +} diff --git a/ios/KataGo iOS/KataGo iOSTests/KataGo_iOSTests.swift b/ios/KataGo iOS/KataGo iOSTests/KataGo_iOSTests.swift new file mode 100644 index 000000000..3c58d0256 --- /dev/null +++ b/ios/KataGo iOS/KataGo iOSTests/KataGo_iOSTests.swift @@ -0,0 +1,36 @@ +// +// KataGo_iOSTests.swift +// KataGo iOSTests +// +// Created by Chin-Chang Yang on 2023/7/2. +// + +import XCTest +@testable import KataGo_iOS + +final class KataGo_iOSTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/ios/KataGo iOS/KataGo iOSUITests/KataGo_iOSUITests.swift b/ios/KataGo iOS/KataGo iOSUITests/KataGo_iOSUITests.swift new file mode 100644 index 000000000..f33ccdc50 --- /dev/null +++ b/ios/KataGo iOS/KataGo iOSUITests/KataGo_iOSUITests.swift @@ -0,0 +1,41 @@ +// +// KataGo_iOSUITests.swift +// KataGo iOSUITests +// +// Created by Chin-Chang Yang on 2023/7/2. +// + +import XCTest + +final class KataGo_iOSUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/ios/KataGo iOS/KataGo iOSUITests/KataGo_iOSUITestsLaunchTests.swift b/ios/KataGo iOS/KataGo iOSUITests/KataGo_iOSUITestsLaunchTests.swift new file mode 100644 index 000000000..186e7e2d2 --- /dev/null +++ b/ios/KataGo iOS/KataGo iOSUITests/KataGo_iOSUITestsLaunchTests.swift @@ -0,0 +1,32 @@ +// +// KataGo_iOSUITestsLaunchTests.swift +// KataGo iOSUITests +// +// Created by Chin-Chang Yang on 2023/7/2. +// + +import XCTest + +final class KataGo_iOSUITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +} diff --git a/ios/KataGo iOS/KataGoSwift/KataGoSwift.h b/ios/KataGo iOS/KataGoSwift/KataGoSwift.h new file mode 100644 index 000000000..c6360181f --- /dev/null +++ b/ios/KataGo iOS/KataGoSwift/KataGoSwift.h @@ -0,0 +1,18 @@ +// +// KataGoSwift.h +// KataGoSwift +// +// Created by Chin-Chang Yang on 2023/11/18. +// + +#import + +//! Project version number for KataGoSwift. +FOUNDATION_EXPORT double KataGoSwiftVersionNumber; + +//! Project version string for KataGoSwift. +FOUNDATION_EXPORT const unsigned char KataGoSwiftVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/ios/KataGo iOS/Resources/default_gtp.cfg b/ios/KataGo iOS/Resources/default_gtp.cfg new file mode 100644 index 000000000..1c0cba46c --- /dev/null +++ b/ios/KataGo iOS/Resources/default_gtp.cfg @@ -0,0 +1,493 @@ +# Config for KataGo C++ GTP engine, i.e. "./katago.exe gtp" + +# RUNNING ON AN ONLINE SERVER OR IN A REAL TOURNAMENT OR MATCH: +# If you plan to do so, you may want to read through the "Rules" section +# below carefully for proper handling of komi and handicap games and end-of-game cleanup +# and various other details. + +# NOTES ABOUT PERFORMANCE AND MEMORY USAGE: +# You will likely want to tune one or more the following: +# +# numSearchThreads: +# The number of CPU threads to use. If your GPU is powerful, it can actually be much higher than +# the number of cores on your processor because you will need many threads to feed large enough +# batches to make good use of the GPU. +# +# The "./katago benchmark" command can help you tune this parameter, as well as to test out the effect +# of changes to any of the other parameters below! +# +# nnCacheSizePowerOfTwo: +# This controls the NN Cache size, which is the primary RAM/memory use. +# Increase this if you don't mind the memory use and want better performance for searches with +# tens of thousands of visits or more. Decrease this if you want to limit memory usage. +# +# If you're someone who is happy to do a bit of math - each neural net entry takes very +# approximately 1.5KB, except when using whole-board ownership/territory visualizations, each +# entry will take very approximately 3KB. The number of entries is (2 ** nnCacheSizePowerOfTwo), +# for example 2 ** 18 = 262144. +# +# OTHER NOTES: +# If you have more than one GPU, take a look at "OpenCL GPU settings" or "CUDA GPU settings" below. +# +# If using OpenCL, you will want to verify that KataGo is picking up the correct device! +# (e.g. some systems may have both an Intel CPU OpenCL and GPU OpenCL, if KataGo appears to pick +# the wrong one, you correct this by specifying "openclGpuToUse" below). +# +# You may also want to adjust "maxVisits", "ponderingEnabled", "resignThreshold", and possibly +# other parameters depending on your intended usage. +# +# ---------------------------------------------------------------------------------------- + +# For the `katago gtp` command, ALL of THE BELOW VALUES MAY BE SET OR OVERRIDDEN if desired via +# the command line arguments: +# -override-config KEY=VALUE,KEY=VALUE,... + +# Logs and files-------------------------------------------------------------------------- + +# Where to output log? +# logDir = gtp_logs # Each run of KataGo will log to a separate file in this dir +# logDirDated = gtp_logs # Use this instead of logDir to also write separate dated subdirs +# logFile = gtp.log # Use this instead of logDir to just specify a single file directly + +# Logging options +logAllGTPCommunication = true +logSearchInfo = true +logToStderr = false + +# KataGo will display some info to stderr on GTP startup +# Uncomment this to suppress that and remain silent +# startupPrintMessageToStderr = false + +# Chat some stuff to stderr, for use in things like malkovich chat to OGS. +# ogsChatToStderr = true + +# Optionally override where KataGo will attempt to save things like openCLTuner files and other cached data. +# homeDataDir = DIRECTORY + +# Analysis------------------------------------------------------------------------------------ + +# Configure the maximum length of analysis printed out by lz-analyze and other places. +# Controls the number of moves after the first move in a variation. +analysisPVLen = 1 + +# Report winrates for chat and analysis as (BLACK|WHITE|SIDETOMOVE). +# Default is SIDETOMOVE, which is what tools that use LZ probably also expect +# reportAnalysisWinratesAs = SIDETOMOVE + +# Larger values will make KataGo explore the top move(s) less deeply and accurately, +# but explore and give evaluations to a greater variety of moves, for analysis (does NOT affect play). +# Defaults to 0.04. +# An extreme value like 1 will distribute many playouts across every move on the board, even very bad moves. +analysisWideRootNoise = 0.04 + + +# Default rules------------------------------------------------------------------------------------ +# See https://lightvector.github.io/KataGo/rules.html for a description of the rules. +# These rules are defaults and can be changed mid-run by several custom GTP commands. +# See https://github.com/lightvector/KataGo/blob/master/docs/GTP_Extensions.md for those commands. + +# Some other legal values are: "chinese", "japanese", "korean", "aga", "chinese-ogs", "new-zealand". +# KataGo does not claim to exactly match any particular human ruleset, but KataGo will try to behave +# as closely as possible given the rules it has implemented. +rules = tromp-taylor + +# Use the below instead to specify an arbitrary combination of individual rules. + +# koRule = SIMPLE # Simple ko rules (triple ko = no result) +# koRule = POSITIONAL # Positional superko +# koRule = SITUATIONAL # Situational superko + +# scoringRule = AREA # Area scoring +# scoringRule = TERRITORY # Territory scoring (uses a sort of special computer-friendly territory ruleset) + +# taxRule = NONE # All surrounded empty points are scored +# taxRule = SEKI # Eyes in seki do NOT count as points +# taxRule = ALL # All groups are taxed up to 2 points for the two eyes needed to live + +# multiStoneSuicideLegal = true # Is multiple-stone suicide legal? (Single-stone suicide is always illegal). + +# hasButton = false # Set to true when area scoring to award 0.5 points to the first pass. + +# friendlyPassOk = true # Set to true except for computer rulesets that requires capturing all stones before passing. + +# whiteHandicapBonus = 0 # In handicap games, give white no compensation for black's handicap stones (Tromp-taylor, NZ, JP) +# whiteHandicapBonus = N-1 # In handicap games, give white N-1 points for black's handicap stones (AGA) +# whiteHandicapBonus = N # In handicap games, give white N points for black's handicap stones (Chinese) + +# Uncomment and change to adjust what board size KataGo uses upon startup by default if GTP doesn't specify. +# defaultBoardSize = 19 +# Specify this to force a particular komi, EVEN if the GUI or GTP controller tries to set a different one +# ignoreGTPAndForceKomi = 7 + +# Bot behavior--------------------------------------------------------------------------------------- + +# Resignation ------------- + +# Resignation occurs if for at least resignConsecTurns in a row, +# the winLossUtility (which is on a [-1,1] scale) is below resignThreshold. +allowResignation = false +resignThreshold = -0.90 +resignConsecTurns = 3 +# Uncomment to make katago not resign close games, behind by fewer than this many points +# resignMinScoreDifference = 10 + +# Handicap ------------- + +# Assume that if black makes many moves in a row right at the start of the game, then the game is a handicap game. +# This is necessary on some servers and for some GUIs and also when initializing from many SGF files, which may +# set up a handicap game using repeated GTP "play" commands for black rather than GTP "place_free_handicap" commands. +# However, it may also lead to incorrect understanding of komi if whiteHandicapBonus is used and a server does NOT +# have such a practice. +# Defaults to true! Uncomment and set to false to disable this behavior. +# assumeMultipleStartingBlackMovesAreHandicap = true + +# Makes katago dynamically adjust in handicap or altered-komi games to assume based on those game settings that it +# must be stronger or weaker than the opponent and to play accordingly. Greatly improves handicap +# strength by biasing winrates and scores to favor appropriate safe/aggressive play. +# Does NOT affect analysis (lz-analyze, kata-analyze, used by programs like Lizzie) so analysis remains unbiased. +# Uncomment and set this to 0 to disable this and make KataGo play the same always. +# dynamicPlayoutDoublingAdvantageCapPerOppLead = 0.045 + +# Instead of a dynamic level, you can uncomment this and set this to a value from -3.0 to 3.0 to set KataGo's aggression to a FIXED level. +# DOES affect analysis tools (lz-analyze, kata-analyze, used by programs like Lizzie). +# Negative makes KataGo behave as if it is much weaker than the opponent, preferring to play defensively. +# Positive makes KataGo behave as if it is much stronger than the opponent, prefering to play aggressively or even overplay slightly. +# If this and "dynamicPlayoutDoublingAdvantageCapPerOppLead" are BOTH set then dynamic will be used for all games and this fixed +# value will be used for analysis tools. +# playoutDoublingAdvantage = 0.0 + +# Uncommenting one of these will enforce that the FIXED playoutDoublingAdvantage will only apply when KataGo plays the specified color +# and will be negated when playing the opposite color. +# playoutDoublingAdvantagePla = BLACK +# playoutDoublingAdvantagePla = WHITE + +# Passing and cleanup ------------- + +# Make the bot never assume that its pass will end the game, even if passing would end and "win" under Tromp-Taylor rules. +# Usually this is a good idea when using it for analysis or playing on servers where scoring may be implemented non-tromp-taylorly. +# Defaults to true! Uncomment and set to false to disable this. +# conservativePass = true + +# When using territory scoring, self-play games continue beyond two passes with special cleanup +# rules that may be confusing for human players. This option prevents the special cleanup phases from being +# reachable when using the bot for GTP play. +# Defaults to true! Uncomment and set to false if you want KataGo to be able to enter special cleanup. +# For example, if you are testing it against itself, or against another bot that has precisely implemented the rules +# documented at https://lightvector.github.io/KataGo/rules.html +# preventCleanupPhase = true + +# Misc Behavior -------------------- + +# If the board is symmetric, search only one copy of each equivalent move. Attempts to also account for ko/superko, will not theoretically perfect for superko. +# Uncomment and set to false to disable this. +# rootSymmetryPruning = true + +# Uncomment and set to true to make KataGo avoid a particular joseki that some KataGo nets misevaluate, +# and also to improve opening diversity versus some particular other bots that like to play it all the time. +# avoidMYTDaggerHack = false + +# Have KataGo mildly prefer to avoid playing the same joseki in every corner of the board. +# Uncomment to set to a specific value. Otherwise, defaults to 0 in even games, and to 0.005 in handicap games. +# See also the Avoid SGF mechanism at the bottom of this config. +# avoidRepeatedPatternUtility = 0.0 + +# Experimental logic to make KataGo fight a bit against mirror Go even with unfavorable komi. +# Enabled by default for GTP play, disabled for GTP analysis (i.e lizzie) and analysis engine. +# Uncomment and set to true to enable it for analysis, or false to disable it fully. +# antiMirror = true + +# Search limits----------------------------------------------------------------------------------- + +# For all of "maxVisits", "maxPlayouts", "maxTime", search will still try to follow GTP time controls and may make a move +# faster than the specified max if GTP tells it that it is playing under a clock as well in the current game. + +# If provided, limit maximum number of root visits per search to this much. (With tree reuse, visits do count earlier search) +# maxVisits = 500 +# If provided, limit maximum number of new playouts per search to this much. (With tree reuse, playouts do not count earlier search) +# maxPlayouts = 300 +# If provided, cap search time at this many seconds. +maxTime = 0.1 + +# Ponder on the opponent's turn? +ponderingEnabled = false +maxTimePondering = 60 # Maximum time to ponder, in seconds. Comment out to make unlimited. +# Note: you can set "maxVisitsPondering" or "maxPlayoutsPondering" too. + +# Approx number of seconds to buffer for lag for GTP time controls - will move a bit faster assuming there is this much lag per move. +lagBuffer = 1.0 + +# Number of threads to use in search +numSearchThreads = 32 + +# Play a little faster if the opponent is passing, for friendliness +searchFactorAfterOnePass = 0.50 +searchFactorAfterTwoPass = 0.25 +# Play a little faster if super-winning, for friendliness +searchFactorWhenWinning = 0.40 +searchFactorWhenWinningThreshold = 0.95 + +# GPU Settings------------------------------------------------------------------------------- + +# Maximum number of positions to send to a single GPU at once. +# The default value here is roughly equal to numSearchThreads, but you can specify it manually +# if you are running out of memory, or if you are using multiple GPUs that expect to split +# up the work. +nnMaxBatchSize = 16 + +# Cache up to (2 ** this) many neural net evaluations in case of transpositions in the tree. +# Uncomment and edit to change if you want to adjust a major component of KataGo's RAM usage. +# nnCacheSizePowerOfTwo = 20 + +# Size of mutex pool for nnCache is (2 ** this). +# nnMutexPoolSizePowerOfTwo = 16 + +# Randomize board orientation when running neural net evals? Uncomment and set to false to disable. +# nnRandomize = true +# If provided, force usage of a specific seed for nnRandomize instead of randomizing. +# nnRandSeed = abcdefg + +# TO USE MULTIPLE GPUS: +# Metal + CoreML backends hack here. +# Metal backend runs the default GPU 0. +# CoreML backend runs at another two threads. +# So, if you want to use Metal + CoreML, you should set numNNServerThreadsPerModel to 3. +numNNServerThreadsPerModel = 1 + + +# TENSORRT GPU settings-------------------------------------- +# These only apply when using the TENSORRT version of KataGo. + +# IF USING ONE GPU: optionally uncomment and change this if the GPU you want to use turns out to be not device 0 +# trtDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines (AND set numNNServerThreadsPerModel above): +# trtDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# trtDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 + +# IF USING THREE GPUS: Uncomment these three lines (AND set numNNServerThreadsPerModel above): +# trtDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# trtDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 +# trtDeviceToUseThread2 = 2 # change this if the third GPU you want to use turns out to be not device 2 + +# You can probably guess the pattern if you have four, five, etc. GPUs. + + +# CUDA GPU settings-------------------------------------- +# These only apply when using the CUDA version of KataGo. + +# IF USING ONE GPU: optionally uncomment and change this if the GPU you want to use turns out to be not device 0 +# cudaDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines (AND set numNNServerThreadsPerModel above): +# cudaDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# cudaDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 + +# IF USING THREE GPUS: Uncomment these three lines (AND set numNNServerThreadsPerModel above): +# cudaDeviceToUseThread0 = 0 # change this if the first GPU you want to use turns out to be not device 0 +# cudaDeviceToUseThread1 = 1 # change this if the second GPU you want to use turns out to be not device 1 +# cudaDeviceToUseThread2 = 2 # change this if the third GPU you want to use turns out to be not device 2 + +# You can probably guess the pattern if you have four, five, etc. GPUs. + +# KataGo will automatically use FP16 or not based on the compute capability of your NVIDIA GPU. If you +# want to try to force a particular behavior though you can uncomment these lines and change them +# to "true" or "false". E.g. it's using FP16 but on your card that's giving an error, or it's not using +# FP16 but you think it should. +# cudaUseFP16 = auto +# cudaUseNHWC = auto + + +# OpenCL GPU settings-------------------------------------- +# These only apply when using the OpenCL version of KataGo. + +# Uncomment to tune OpenCL for every board size separately, rather than only the largest possible size +# openclReTunePerBoardSize = true + +# IF USING ONE GPU: optionally uncomment and change this if the best device to use is guessed incorrectly. +# The default behavior tries to guess the 'best' GPU or device on your system to use, usually it will be a good guess. +# openclDeviceToUse = 0 + +# IF USING TWO GPUS: Uncomment these two lines and replace X and Y with the device ids of the devices you want to use. +# It might NOT be 0 and 1, some computers will have many OpenCL devices. You can see what the devices are when +# KataGo starts up - it should print or log all the devices it finds. +# (AND also set numNNServerThreadsPerModel above) +# openclDeviceToUseThread0 = X +# openclDeviceToUseThread1 = Y + +# IF USING THREE GPUS: Uncomment these three lines and replace X and Y and Z with the device ids of the devices you want to use. +# It might NOT be 0 and 1 and 2, some computers will have many OpenCL devices. You can see what the devices are when +# KataGo starts up - it should print or log all the devices it finds. +# (AND also set numNNServerThreadsPerModel above) +# openclDeviceToUseThread0 = X +# openclDeviceToUseThread1 = Y +# openclDeviceToUseThread2 = Z + +# You can probably guess the pattern if you have four, five, etc. GPUs. + +# KataGo will automatically use FP16 or not based on testing your GPU during tuning. If you +# want to try to force a particular behavior though you can uncomment this lines and change it +# to "true" or "false". This is a fairly blunt setting - more detailed settings are testable +# by rerunning the tuner with various arguments. +# openclUseFP16 = auto + + +# Eigen-specific settings-------------------------------------- +# These only apply when using the Eigen (pure CPU) version of KataGo. + +# This is the number of CPU threads for evaluating the neural net on the Eigen backend. +# It defaults to numSearchThreads. +# numEigenThreadsPerModel = X + +# CoreML settings-------------------------------------- +# These only apply when using the CoreML version of KataGo. + +# IF USING ONE MODEL: +# coremlDeviceToUse = 0 # GPU +coremlDeviceToUse = 100 # Neural Engine + +# IF USING TWO MODEL: Uncomment these two lines +# (AND also set numNNServerThreadsPerModel = 2 above) +# coremlDeviceToUseThread0 = 0 # GPU +# coremlDeviceToUseThread1 = 100 # Neural Engine + +# IF USING THREE MODEL: Uncomment these three lines +# (AND also set numNNServerThreadsPerModel = 3 above) +# coremlDeviceToUseThread0 = 0 # GPU +# coremlDeviceToUseThread1 = 100 # Neural Engine +# coremlDeviceToUseThread2 = 101 # Neural Engine + +# If you want to force the backend using float-point 16-bit or 32-bit, you can uncomment +# this lines and change it to "true" or "false". +# coremlUseFP16 = auto + +# You can probably guess the pattern if you have four, five, etc. Models. + +# Root move selection and biases------------------------------------------------------------------------------ +# Uncomment and edit any of the below values to change them from their default. + +# If provided, force usage of a specific seed for various things in the search instead of randomizing +# searchRandSeed = hijklmn + +# Temperature for the early game, randomize between chosen moves with this temperature +# chosenMoveTemperatureEarly = 0.5 +# Decay temperature for the early game by 0.5 every this many moves, scaled with board size. +# chosenMoveTemperatureHalflife = 19 +# At the end of search after the early game, randomize between chosen moves with this temperature +# chosenMoveTemperature = 0.10 +# Subtract this many visits from each move prior to applying chosenMoveTemperature +# (unless all moves have too few visits) to downweight unlikely moves +# chosenMoveSubtract = 0 +# The same as chosenMoveSubtract but only prunes moves that fall below the threshold, does not affect moves above +# chosenMovePrune = 1 + +# Number of symmetries to sample (WITHOUT replacement) and average at the root +# rootNumSymmetriesToSample = 1 + +# Using LCB for move selection? +# useLcbForSelection = true +# How many stdevs a move needs to be better than another for LCB selection +# lcbStdevs = 5.0 +# Only use LCB override when a move has this proportion of visits as the top move +# minVisitPropForLCB = 0.15 + +# Internal params------------------------------------------------------------------------------ +# Uncomment and edit any of the below values to change them from their default. + +# Scales the utility of winning/losing +# winLossUtilityFactor = 1.0 +# Scales the utility for trying to maximize score +# staticScoreUtilityFactor = 0.10 +# dynamicScoreUtilityFactor = 0.30 +# Adjust dynamic score center this proportion of the way towards zero, capped at a reasonable amount. +# dynamicScoreCenterZeroWeight = 0.20 +# dynamicScoreCenterScale = 0.75 +# The utility of getting a "no result" due to triple ko or other long cycle in non-superko rulesets (-1 to 1) +# noResultUtilityForWhite = 0.0 +# The number of wins that a draw counts as, for white. (0 to 1) +# drawEquivalentWinsForWhite = 0.5 + +# Exploration constant for mcts +# cpuctExploration = 1.0 +# cpuctExplorationLog = 0.45 + +# Parameters that control exploring more in volatile positions, exploring less in stable positions. +# cpuctUtilityStdevPrior = 0.40 +# cpuctUtilityStdevPriorWeight = 2.0 +# cpuctUtilityStdevScale = 0.85 + +# FPU reduction constant for mcts +# fpuReductionMax = 0.2 +# rootFpuReductionMax = 0.1 +# fpuParentWeightByVisitedPolicy = true + +# Parameters that control weighting of evals based on the net's own self-reported uncertainty. +# useUncertainty = true +# uncertaintyExponent = 1.0 +# uncertaintyCoeff = 0.25 + +# Amount to apply a downweighting of children with very bad values relative to good ones +# valueWeightExponent = 0.25 + +# Slight incentive for the bot to behave human-like with regard to passing at the end, filling the dame, +# not wasting time playing in its own territory, etc, and not play moves that are equivalent in terms of +# points but a bit more unfriendly to humans. +# rootEndingBonusPoints = 0.5 + +# Make the bot prune useless moves that are just prolonging the game to avoid losing yet +# rootPruneUselessMoves = true + +# Apply bias correction based on local pattern keys +# subtreeValueBiasFactor = 0.45 +# subtreeValueBiasWeightExponent = 0.85 + +# Use graph search rather than tree search - identify and share search for transpositions. +# useGraphSearch = true + +# How much to shard the node table for search synchronization +# nodeTableShardsPowerOfTwo = 16 +# How many virtual losses to add when a thread descends through a node +# numVirtualLossesPerThread = 1 + +# Improve the quality of evals under heavy multithreading +# useNoisePruning = true + + +# Avoid SGF Patterns ------------------------------------------------------------------------------ +# The parameters in this section provide a powerful way to customize KataGo to avoid moves that follow specific patterns +# based on a set of provided SGF files loaded upon startup. Uncomment them to use this feature. +# Additionally, if the SGF file contains the string %SKIP% in a comment on a move, that move will be ignored for this purpose. + +# Load sgf files from this directory when the engine is started (ONLY on startup, will not reload unless engine is restarted) +# avoidSgfPatternDirs = path/to/directory/with/sgfs/ + +# Penalize this much utility per matching move. +# Set this negative if you instead want to make KataGo favor the SGF patterns instead of penalizing it! +# This number does not need to be large, even 0.001 will make a difference. Too-large values may lead to bad play. +# avoidSgfPatternUtility = 0.001 + +# Optional - load only the newest this many files +# avoidSgfPatternMaxFiles = 20 + +# Optional - Penalty is multiplied by this per each older SGF file, so that old sgf files matter less than newer ones. +# avoidSgfPatternLambda = 0.90 + +# Optional - pay attention only to moves that were made by players with this name. +# For example you can set it to the name that your bot's past games will show up as in the SGF, so that the bot will only avoid repeating +# moves that itself made in past games, not the moves that its opponents made. +# avoidSgfPatternAllowedNames = my-ogs-bot-name1,my-ogs-bot-name2 + +# Optional - Ignore any moves in SGF files that occurred before this turn number. +# avoidSgfPatternMinTurnNumber = 0 + +# For more avoid patterns: +# You can also specify a second set of parameters, and a third, fourth, etc by numbering 2,3,4,... +# avoidSgf2PatternDirs = ... +# avoidSgf2PatternUtility = ... +# avoidSgf2PatternMaxFiles = ... +# avoidSgf2PatternLambda = ... +# avoidSgf2PatternAllowedNames = ... +# avoidSgf2PatternMinTurnNumber = ... + + + + diff --git a/python/convert_coreml.py b/python/convert_coreml.py new file mode 100644 index 000000000..668234535 --- /dev/null +++ b/python/convert_coreml.py @@ -0,0 +1,93 @@ +#!/usr/bin/python3 +# Example usage: +# wget https://media.katagotraining.org/uploaded/networks/zips/kata1/kata1-b40c256-s11840935168-d2898845681.zip +# unzip kata1-b40c256-s11840935168-d2898845681.zip +# python python/convert_coreml.py -saved-model-dir kata1-b40c256-s11840935168-d2898845681/saved_model -name-scope swa_model -board_size 19 + +import argparse +import json +import tensorflow as tf + +from model import Model + +import common +import tempfile +import os +from tensorflow.python.tools.freeze_graph import freeze_graph +import coremltools as ct + +description = """ +Convert a trained neural net to a CoreML model. +""" + +parser = argparse.ArgumentParser(description=description) +common.add_model_load_args(parser) +parser.add_argument('-name-scope', help='Name scope for model variables', required=False) +parser.add_argument('-board-size', help='Board size of model', required=False) +args = vars(parser.parse_args()) + +(model_variables_prefix, model_config_json) = common.load_model_paths(args) +name_scope = args["name_scope"] +pos_len = int(args["board_size"]) + +if pos_len is None: + pos_len = 19 + +# Model ---------------------------------------------------------------- + +with open(model_config_json) as f: + model_config = json.load(f) + +if name_scope is not None: + with tf.compat.v1.variable_scope(name_scope): + model = Model(model_config,pos_len,{}) +else: + model = Model(model_config,pos_len,{}) + +saver = tf.compat.v1.train.Saver( + max_to_keep = 10000, + save_relative_paths = True, +) + +model_dir = tempfile.mkdtemp() +graph_def_file = os.path.join(model_dir, 'tf_graph.pb') +checkpoint_file = os.path.join(model_dir, 'tf_model.ckpt') +frozen_graph_file = os.path.join(model_dir, 'KataGoModel.pb') +mlmodel_file = f'KataGoModel{pos_len}x{pos_len}.mlmodel' + +output_names = [ + model.policy_output.op.name, + model.value_output.op.name, + model.ownership_output.op.name, + model.miscvalues_output.op.name, + model.moremiscvalues_output.op.name +] + +print(output_names) +with tf.compat.v1.Session() as session: + saver.restore(session, model_variables_prefix) + + tf.train.write_graph(session.graph, model_dir, graph_def_file, as_text=False) + # save the weights + saver = tf.train.Saver() + saver.save(session, checkpoint_file) + + # take the graph definition and weights + # and freeze into a single .pb frozen graph file + freeze_graph(input_graph=graph_def_file, + input_saver="", + input_binary=True, + input_checkpoint=checkpoint_file, + output_node_names=','.join(output_names), + restore_op_name="save/restore_all", + filename_tensor_name="save/Const:0", + output_graph=frozen_graph_file, + clear_devices=True, + initializer_nodes="") + + mlmodel = ct.convert(frozen_graph_file) + mlmodel.short_description = f'KataGo {pos_len}x{pos_len} model version {model.version} converted from {model_config_json}' + mlmodel.version = f'{model.version}' + mlmodel.save(mlmodel_file) + + print("Core ML model saved at {}".format(mlmodel_file)) diff --git a/python/convert_coreml_pytorch.py b/python/convert_coreml_pytorch.py new file mode 100644 index 000000000..0e4176493 --- /dev/null +++ b/python/convert_coreml_pytorch.py @@ -0,0 +1,394 @@ +#!/usr/bin/python3 +""" +Convert a trained PyTorch neural network to a CoreML model. + +Example usage: + python3 convert_coreml_pytorch.py -checkpoint b18c384nbt-uec-20221121b.ckpt -use-swa -nbits 8 +""" + +import argparse +import sys +from typing import Optional, Tuple + +import torch +import coremltools as ct +import coremlmish + +from katago.train.load_model import load_model +from coremltools.optimize.coreml import ( + OptimizationConfig, + OpMagnitudePrunerConfig, + OpPalettizerConfig, + prune_weights, + palettize_weights, + OpLinearQuantizerConfig, + linear_quantize_weights, +) + + +def print_versions(): + """Print versions of torch, coremltools, and coremlmish.""" + print(f"torch version: {torch.__version__}") + print(f"coremltools version: {ct.__version__}") + # Assuming coremlmish has an attribute __function__; adjust if necessary + function_name = getattr(coremlmish, "__function__", "Unknown") + print(f"Using coremlmish function: {function_name}") + + +def parse_arguments() -> argparse.Namespace: + """Parse command-line arguments.""" + parser = argparse.ArgumentParser( + description="Convert a trained neural net to a CoreML model." + ) + + parser.add_argument( + "-checkpoint", + required=True, + help="Path to the model checkpoint file.", + ) + parser.add_argument( + "-use-swa", + action="store_true", + help="Use SWA (Stochastic Weight Averaging) model.", + ) + parser.add_argument( + "-pos-len", + type=int, + default=19, + help="Position length (default: 19).", + ) + parser.add_argument( + "-batch-size", + type=int, + default=1, + help="Batch size (default: 1).", + ) + parser.add_argument( + "-fp32", + action="store_true", + help="Use 32-bit floating-point precision (default: FLOAT16).", + ) + parser.add_argument( + "-nbits", + type=int, + choices=[8, 6, 4, 3, 2, 1], + help="Number of bits for palettizing the weights (e.g., 8).", + ) + parser.add_argument( + "-sparsity", + type=float, + default=0.0, + help="Target sparsity for pruning the weights (default: 0.0).", + ) + parser.add_argument( + "-output", + required=False, + help="Path to the converted Core ML package.", + ) + + return parser.parse_args() + + +def load_traced_model( + func: torch.nn.Module, + example_inputs: Tuple[torch.Tensor, ...], +) -> torch.jit.ScriptModule: + """Trace the PyTorch model using TorchScript.""" + print("Tracing model ...") + traced = torch.jit.trace(func, example_inputs) + return traced + + +def prepare_example_inputs( + model, + batch_size: int, +) -> Tuple[torch.Tensor, ...]: + """Prepare example inputs for tracing the model.""" + input_spatial = torch.rand( + batch_size, + model.bin_input_shape[0], + model.bin_input_shape[1], + model.bin_input_shape[2], + ) + input_global = torch.rand(batch_size, model.global_input_shape[0]) + input_meta = ( + torch.rand(batch_size, model.metadata_encoder.c_input) + if model.metadata_encoder + else None + ) + + if input_meta is not None: + return (input_spatial, input_global, input_meta) + return (input_spatial, input_global) + + +def convert_to_coreml( + traced_model: torch.jit.ScriptModule, + model, + input_shapes: Tuple[torch.Size, ...], + compute_precision: ct.precision, + minimum_deployment_target: Optional[ct.target], +) -> ct.models.MLModel: + """Convert the traced PyTorch model to CoreML format.""" + inputs = [ct.TensorType(shape=shape) for shape in input_shapes] + + print("Converting model ...") + mlmodel = ct.convert( + traced_model, + convert_to="mlprogram", + inputs=inputs, + compute_precision=compute_precision, + minimum_deployment_target=minimum_deployment_target, + ) + + return mlmodel + + +def rename_features(spec, old_name: str, new_name: str): + """Rename a feature in the CoreML model spec.""" + ct.utils.rename_feature(spec, old_name, new_name) + + +def apply_optimizations( + mlmodel: ct.models.MLModel, + sparsity: float, + nbits: Optional[int], + joint_compression: bool, +) -> Tuple[ct.models.MLModel, str]: + """Apply pruning and quantization optimizations to the CoreML model.""" + spec = mlmodel._spec + compression_description = "" + + # Apply sparsity pruning if requested + if sparsity > 0: + sparsity_config = OpMagnitudePrunerConfig(target_sparsity=sparsity) + pruning_config = OptimizationConfig(global_config=sparsity_config) + + print(f"Pruning weights with {sparsity} sparsity ...") + mlmodel = prune_weights(mlmodel, config=pruning_config) + compression_description += f"sparsity {sparsity} " + + # Apply quantization or palettization if nbits is specified + if nbits is not None: + if nbits == 8: + threshold_config = OpLinearQuantizerConfig( + mode="linear", + ) + quantizing_config = OptimizationConfig(global_config=threshold_config) + + print(f"Quantizing weights to {nbits} bits ...") + mlmodel = linear_quantize_weights( + mlmodel, + config=quantizing_config, + joint_compression=joint_compression, + ) + else: + palettizing_config = OptimizationConfig( + global_config=OpPalettizerConfig( + nbits=nbits, + mode="kmeans", + granularity="per_grouped_channel", + group_size=4, + ) + ) + + print(f"Palettizing weights with {nbits} bit(s) ...") + mlmodel = palettize_weights( + mlmodel, + palettizing_config, + joint_compression=joint_compression, + ) + + compression_description += f"quantization bits {nbits} " + + return mlmodel, compression_description + + +def update_model_metadata( + mlmodel: ct.models.MLModel, + pos_len: int, + precision_name: str, + version: int, + sparsity_description: str, + compression_description: str, + meta_encoder_version: int, + checkpoint_file: str, +) -> None: + """Update the metadata and description of the CoreML model.""" + description = ( + f"KataGo {pos_len}x{pos_len} compute " + f"precision {precision_name} model version {version} " + f"{sparsity_description}" + f"{compression_description}" + f"meta encoder version {meta_encoder_version} " + f"converted from {checkpoint_file}" + ) + mlmodel.short_description = description + mlmodel.version = f"{version}" + + +def save_coreml_model( + mlmodel: ct.models.MLModel, + pos_len: int, + precision_name: str, + meta_encoder_version: int, + output_path: str, +) -> str: + """Save the CoreML model to a file and return the file path.""" + if output_path is None: + meta_encoder_suffix = f"m{meta_encoder_version}" if meta_encoder_version > 0 else "" + filename = ( + f"KataGoModel{pos_len}x{pos_len}{precision_name}{meta_encoder_suffix}.mlpackage" + ) + else: + filename = output_path + + print("Saving model ...") + mlmodel.save(filename) + print(f"Saved Core ML model at {filename}") + + return filename + + +def main(): + """Main function to convert PyTorch model to CoreML.""" + print_versions() + + args = parse_arguments() + + checkpoint_file = args.checkpoint + use_swa = args.use_swa + pos_len = args.pos_len + batch_size = args.batch_size + fp32 = args.fp32 + nbits = args.nbits + sparsity = args.sparsity + output_path = args.output + + # Load the model + model, swa_model, _ = load_model( + checkpoint_file=checkpoint_file, + use_swa=use_swa, + device="cpu", + pos_len=pos_len, + for_coreml=True, + verbose=True, + ) + + # Select the appropriate model + func = swa_model if swa_model is not None else model + print(f"Using model: {func.__class__.__name__}") + + # Determine meta encoder version + meta_encoder_version = ( + 0 + if model.metadata_encoder is None + else ( + 1 + if "meta_encoder_version" not in model.config["metadata_encoder"] + else model.config["metadata_encoder"]["meta_encoder_version"] + ) + ) + print(f"Meta encoder version: {meta_encoder_version}") + + # Determine model version with workaround + version = model.config.get("version", 0) + if meta_encoder_version > 0: + version = max(version, 15) + print(f"Model version: {version}") + + # Prepare example inputs for tracing + example_inputs = prepare_example_inputs(model, batch_size) + + with torch.no_grad(): + func.eval() + traced_model = load_traced_model(func, example_inputs) + + # Determine compute precision + compute_precision = ct.precision.FLOAT32 if fp32 else ct.precision.FLOAT16 + + # Determine minimum deployment target + minimum_deployment_target = ( + ct.target.iOS18 if sparsity or (nbits and nbits != 8) else + ct.target.iOS16 if nbits == 8 else + None + ) + + # Convert traced model to CoreML + mlmodel = convert_to_coreml( + traced_model=traced_model, + model=model, + input_shapes=tuple(input.shape for input in example_inputs), + compute_precision=compute_precision, + minimum_deployment_target=minimum_deployment_target, + ) + + # Rename input features + spec = mlmodel._spec + rename_features(spec, "input_1", "input_global") + input_names = [input.name for input in spec.description.input] + print(f"Input names: {input_names}") + + # Rename output features + output_names = [ + "output_policy", + "out_value", + "out_miscvalue", + "out_moremiscvalue", + "out_ownership", + ] + + for i, new_name in enumerate(output_names): + old_name = spec.description.output[i].name + rename_features(spec, old_name, new_name) + + print(f"Output names: {output_names}") + + # Determine precision name + precision_name = "fp32" if fp32 else "fp16" + + # Apply optimizations + joint_compression = sparsity > 0 + mlmodel, compression_description = apply_optimizations( + mlmodel=mlmodel, + sparsity=sparsity, + nbits=nbits, + joint_compression=joint_compression, + ) + sparsity_description = f"sparsity {sparsity} " if sparsity > 0 else "" + + # Update model metadata + update_model_metadata( + mlmodel=mlmodel, + pos_len=pos_len, + precision_name=precision_name, + version=version, + sparsity_description=sparsity_description, + compression_description=compression_description, + meta_encoder_version=meta_encoder_version, + checkpoint_file=checkpoint_file, + ) + + # Rebuild the model with the updated spec + print("Rebuilding model with updated spec ...") + rebuilt_mlmodel = ct.models.MLModel( + mlmodel._spec, + weights_dir=mlmodel._weights_dir, + ) + + # Save the CoreML model + save_coreml_model( + mlmodel=rebuilt_mlmodel, + pos_len=pos_len, + precision_name=precision_name, + meta_encoder_version=meta_encoder_version, + output_path=output_path, + ) + + +if __name__ == "__main__": + try: + main() + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) diff --git a/python/coremlmish.py b/python/coremlmish.py new file mode 100644 index 000000000..ae360a286 --- /dev/null +++ b/python/coremlmish.py @@ -0,0 +1,94 @@ +from coremltools.converters.mil.frontend.torch.torch_op_registry import _TORCH_OPS_REGISTRY, register_torch_op +from coremltools.converters.mil.frontend.torch.ops import _get_inputs +from coremltools.converters.mil import Builder as mb + +# Remove the original mish function +if "mish" in _TORCH_OPS_REGISTRY: + del _TORCH_OPS_REGISTRY["mish"] + +# Set the function to use +__function__ = "mish_torch_sigmoid" + +# Torch Mish Operator with Sigmoid Approximation that can run on Neural Engine +# +# This function applies the Mish activation function to the input tensor `x`. The Mish function is defined as +# x * tanh(Softplus(x)), where Softplus(x) is typically defined as log(1 + exp(x)). However, to avoid +# computational issues with large values of x in float16 format, a sigmoid-based approximation is used. +# +# Instead of using a conditional operation to switch between log(1 + exp(x)) and x based on a threshold, +# a sigmoid function is utilized to smoothly transition between the standard Softplus function and a linear +# approximation. This approach helps in managing large input values, maintaining numerical stability in +# 16-bit floating point computations. +# +# The threshold for switching between Softplus and linear behavior is set at 10.39, rather than the original 20. +# This modification is made considering that exp(10.39) = 32532.666936, which is within the representable range +# of float16, unlike exp(20) = 485165195.40979004, which exceeds the limits of float16. +# +# Arguments: +# context: An object containing information about the execution context of the function. +# node: An object representing a node in a computation graph. +def mish_torch_sigmoid(context, node): + inputs = _get_inputs(context, node, expected=1) + x = inputs[0] + + threshold = 10.39 + + # Approximating conditional behavior using sigmoid function + sigmoid_threshold = mb.sigmoid(x=mb.sub(x=x, y=threshold)) + + # Approximate implementation of Softplus + softplus_part = mb.softplus(x=mb.minimum(x=x, y=threshold)) + softplus = mb.add(x=mb.mul(x=x, y=sigmoid_threshold), + y=mb.mul(x=softplus_part, y=mb.sub(x=1.0, y=sigmoid_threshold))) + + # Mish(x) = x * tanh(Softplus(x)) + tanh_softplus = mb.tanh(x=softplus) + res = mb.mul(x=x, y=tanh_softplus, name=node.name) + context.add(res) + + +# Torch Mish operator that *could* run on Neural Engine +# +# This function applies the Mish activation function on the input tensor `x`. The Mish function is defined as +# x * tanh(Softplus(x)), where Softplus(x) is defined as log(1 + exp(min(x, 10.39))) if x < 10.39 and x otherwise. +# +# The function uses the `mb` module to perform operations such as `minimum`, `exp`, `add`, `log`, `less`, `select`, +# and `tanh`. +# +# The threshold of softplus is modified to 10.39, which is different from the original 20. This is because +# exp(10.39) = 32532.666936 < 32767.0 < 65504.0, so the result of exp(10.39) can be represented by float16. If the threshold +# of softplus is 20, the result of exp(20) is 485165195.40979004, which is out of range of float16. +# +# Arguments: +# context: an object that contains information about the execution context of the function +# node: an object that represents a node in a computation graph +def mish_torch_ne(context, node): + inputs = _get_inputs(context, node, expected=1) + x = inputs[0] + + threshold = 10.39 + + # Softplus(x) = log(1 + exp(min(x, 10.39))) if x < 10.39 else x + min_x_threshold = mb.minimum(x=x, y=threshold) + exp_min_x_threshold = mb.exp(x=min_x_threshold) + add_exp_min_x_threshold_1 = mb.add(x=exp_min_x_threshold, y=1.0) + log_add_exp_min_x_threshold_1 = mb.log(x=add_exp_min_x_threshold_1) + # less(x, y) = x < y + x_less_than_threshold = mb.less(x=x, y=threshold) + # select(cond, a, b) = a if cond else b + softplus = mb.select(cond=x_less_than_threshold, a=log_add_exp_min_x_threshold_1, b=x) + + # Mish(x) = x * tanh(Softplus(x)) + tanh_softplus = mb.tanh(x=softplus) + res = mb.mul(x=x, y=tanh_softplus, name=node.name) + context.add(res) + + +# Register the function +@register_torch_op +def mish(context, node): + if __function__ == "mish_torch_sigmoid": + mish_torch_sigmoid(context, node) + else: + mish_torch_ne(context, node) + \ No newline at end of file diff --git a/python/export_model_pytorch.py b/python/export_model_pytorch.py index c006085f2..820f61c25 100644 --- a/python/export_model_pytorch.py +++ b/python/export_model_pytorch.py @@ -35,6 +35,7 @@ parser.add_argument('-filename-prefix', help='filename prefix to save to within dir', required=True) parser.add_argument('-use-swa', help='Use SWA model', action="store_true", required=False) parser.add_argument('-export-14-as-15', help='Export model version 14 as 15', action="store_true", required=False) +parser.add_argument('-prune-to-zero', help='Prune all weights to zero to create a null model', action="store_true", required=False) args = vars(parser.parse_args()) @@ -45,6 +46,7 @@ def main(args): filename_prefix = args["filename_prefix"] use_swa = args["use_swa"] export_14_as_15 = args["export_14_as_15"] + prune_to_zero = args["prune_to_zero"] os.makedirs(export_dir,exist_ok=True) @@ -121,8 +123,13 @@ def writestr(s): def write_weights(weights): + if prune_to_zero: + weights_to_write = torch.zeros_like(weights) + else: + weights_to_write = weights + # Little endian - reshaped = np.reshape(weights.detach().numpy(),[-1]) + reshaped = np.reshape(weights_to_write.detach().numpy(), [-1]) num_weights = len(reshaped) writestr("@BIN@") f.write(struct.pack(f'<{num_weights}f',*reshaped)) diff --git a/python/katago/train/load_model.py b/python/katago/train/load_model.py index 6642c758f..1b1732e88 100644 --- a/python/katago/train/load_model.py +++ b/python/katago/train/load_model.py @@ -39,7 +39,7 @@ def load_swa_model_state_dict(state_dict): return swa_model_state_dict -def load_model(checkpoint_file, use_swa, device, pos_len=19, verbose=False): +def load_model(checkpoint_file, use_swa, device, pos_len=19, for_coreml=False, verbose=False): from ..train.model_pytorch import Model from torch.optim.swa_utils import AveragedModel @@ -54,7 +54,7 @@ def load_model(checkpoint_file, use_swa, device, pos_len=19, verbose=False): model_config = json.load(f) logging.info(str(model_config)) - model = Model(model_config,pos_len) + model = Model(model_config,pos_len,for_coreml=for_coreml) model.initialize() # Strip off any "module." from when the model was saved with DDP or other things diff --git a/python/katago/train/model_pytorch.py b/python/katago/train/model_pytorch.py index f4e0a93d1..8ad0d54c9 100644 --- a/python/katago/train/model_pytorch.py +++ b/python/katago/train/model_pytorch.py @@ -375,7 +375,7 @@ def forward(self, x, mask, mask_sum_hw): """ mask_sum_hw_sqrt_offset = torch.sqrt(mask_sum_hw) - 14.0 - layer_mean = torch.sum(x, dim=(2, 3), keepdim=True, dtype=torch.float32) / mask_sum_hw + layer_mean = torch.sum(x, dim=(2, 3), keepdim=True) / mask_sum_hw # All activation functions we use right now are always greater than -1.0, and map 0 -> 0. # So off-board areas will equal 0, and then this max is mask-safe if we assign -1.0 to off-board areas. (layer_max,_argmax) = torch.max((x+(mask-1.0)).view(x.shape[0],x.shape[1],-1).to(torch.float32), dim=2) @@ -404,7 +404,7 @@ def forward(self, x, mask, mask_sum_hw): """ mask_sum_hw_sqrt_offset = torch.sqrt(mask_sum_hw) - 14.0 - layer_mean = torch.sum(x, dim=(2, 3), keepdim=True, dtype=torch.float32) / mask_sum_hw + layer_mean = torch.sum(x, dim=(2, 3), keepdim=True) / mask_sum_hw out_pool1 = layer_mean out_pool2 = layer_mean * (mask_sum_hw_sqrt_offset / 10.0) @@ -1314,7 +1314,6 @@ def __init__(self, c_in, c_p1, c_g1, config, activation): self.act2 = act(activation) self.conv2p = torch.nn.Conv2d(c_p1, self.num_policy_outputs, kernel_size=1, padding="same", bias=False) - def initialize(self): # Scaling so that variance on the p and g branches adds up to 1.0 p_scale = 0.8 @@ -1376,7 +1375,6 @@ def forward(self, x, mask, mask_sum_hw, mask_sum:float, extra_outputs: Optional[ outp = self.act2(outp) outp = self.conv2p(outp) outpolicy = outp - # mask out parts outside the board by making them a huge neg number, so that they're 0 after softmax outpolicy = outpolicy - (1.0 - mask) * 5000.0 # NC(HW) concat with NC1 @@ -1603,7 +1601,7 @@ def forward(self, input_meta, extra_outputs: Optional[ExtraOutputs]): class Model(torch.nn.Module): - def __init__(self, config: modelconfigs.ModelConfig, pos_len: int): + def __init__(self, config: modelconfigs.ModelConfig, pos_len: int, for_coreml: bool = False): super(Model, self).__init__() self.config = config @@ -1621,6 +1619,7 @@ def __init__(self, config: modelconfigs.ModelConfig, pos_len: int): self.num_scorebeliefs = config["num_scorebeliefs"] self.num_total_blocks = len(self.block_kind) self.pos_len = pos_len + self.for_coreml = for_coreml if config["version"] <= 12: self.td_score_multiplier = 20.0 @@ -1916,6 +1915,8 @@ def forward( # print("TENSOR BEFORE TRUNK") # print(out) + self.has_intermediate_head = False if self.for_coreml else self.has_intermediate_head + if self.has_intermediate_head: count = 0 for block in self.blocks[:self.intermediate_head_blocks]: @@ -2001,6 +2002,14 @@ def forward( iout_scorebelief_logprobs, ), ) + elif self.for_coreml: + return (( + out_policy, + out_value, + out_miscvalue, + out_moremiscvalue, + out_ownership, + ),) else: return (( out_policy, diff --git a/python/selfplay/export_model_for_selfplay.sh b/python/selfplay/export_model_for_selfplay.sh index eac659be7..24152a647 100755 --- a/python/selfplay/export_model_for_selfplay.sh +++ b/python/selfplay/export_model_for_selfplay.sh @@ -34,7 +34,7 @@ function exportStuff() { TODIR="$2" #Sort by timestamp so that we process in order of oldest to newest if there are multiple - for FILEPATH in $(find "$BASEDIR"/"$FROMDIR"/ -mindepth 1 -maxdepth 1 -printf "%T@ %p\n" | sort -n | cut -d ' ' -f 2) + for FILEPATH in $(gfind "$BASEDIR"/"$FROMDIR"/ -mindepth 1 -maxdepth 1 -printf "%T@ %p\n" | sort -n | cut -d ' ' -f 2) do #Make sure to skip tmp directories that are transiently there by the training, #they are probably in the process of being written @@ -64,19 +64,25 @@ function exportStuff() { mkdir "$TMPDST" set -x - python3 ./export_model_pytorch.py \ + python ./export_model_pytorch.py \ -checkpoint "$SRC"/model.ckpt \ -export-dir "$TMPDST" \ -model-name "$NAMEPREFIX""-""$NAME" \ -filename-prefix model \ -use-swa - python3 ./clean_checkpoint.py \ + python ./clean_checkpoint.py \ -checkpoint "$SRC"/model.ckpt \ -output "$TMPDST"/model.ckpt set +x rm -r "$SRC" + + python ./convert_coreml_pytorch.py \ + -checkpoint "$TMPDST"/model.ckpt \ + -output "$TMPDST"/KataGoModel19x19fp16.mlpackage \ + -use-swa + gzip "$TMPDST"/model.bin #Make a bunch of the directories that selfplay will need so that there isn't a race on the selfplay diff --git a/python/selfplay/shuffle.sh b/python/selfplay/shuffle.sh index 467425380..ad10aa72d 100755 --- a/python/selfplay/shuffle.sh +++ b/python/selfplay/shuffle.sh @@ -36,7 +36,7 @@ echo "Beginning shuffle at" $(date "+%Y-%m-%d %H:%M:%S") if [[ -n "${SKIP_VALIDATE:-}" ]] then ( - time python3 ./shuffle.py \ + time python ./shuffle.py \ "$BASEDIR"/selfplay/ \ -expand-window-per-row 0.4 \ -taper-window-exponent 0.65 \ @@ -56,7 +56,7 @@ then else # Randomly peels off 5% of files generated by selfplay as validation data ( - time python3 ./shuffle.py \ + time python ./shuffle.py \ "$BASEDIR"/selfplay/ \ -expand-window-per-row 0.4 \ -taper-window-exponent 0.65 \ @@ -74,7 +74,7 @@ else wait ) ( - time python3 ./shuffle.py \ + time python ./shuffle.py \ "$BASEDIR"/selfplay/ \ -expand-window-per-row 0.4 \ -taper-window-exponent 0.65 \ @@ -110,7 +110,7 @@ mv "$BASEDIR"/shuffleddata/"$OUTDIR".tmp "$BASEDIR"/shuffleddata/"$OUTDIR" #This should be VERY conservative and allow plenty of time for the training to switch #to newer ones as they get generated. echo "Cleaning up any old dirs" -find "$BASEDIR"/shuffleddata/ -mindepth 1 -maxdepth 1 -type d -mmin +120 | sort | head -n -5 | xargs --no-run-if-empty rm -r +find "$BASEDIR"/shuffleddata/ -mindepth 1 -maxdepth 1 -type d -mmin +120 | sort | ghead -n -5 | xargs --no-run-if-empty rm -r echo "Finished shuffle at" $(date "+%Y-%m-%d %H:%M:%S") #Make a little space between shuffles diff --git a/python/selfplay/shuffle_loop.sh b/python/selfplay/shuffle_loop.sh index fd52e8455..1cd64724f 100755 --- a/python/selfplay/shuffle_loop.sh +++ b/python/selfplay/shuffle_loop.sh @@ -44,7 +44,7 @@ cp -r "$GITROOTDIR"/python/selfplay "$DATED_ARCHIVE" while true do rm -f "$basedir"/selfplay.summary.json.tmp - time python3 ./summarize_old_selfplay_files.py "$basedir"/selfplay/ \ + time python ./summarize_old_selfplay_files.py "$basedir"/selfplay/ \ -old-summary-file-to-assume-correct "$basedir"/selfplay.summary.json \ -new-summary-file "$basedir"/selfplay.summary.json.tmp mv "$basedir"/selfplay.summary.json.tmp "$basedir"/selfplay.summary.json diff --git a/python/selfplay/train.sh b/python/selfplay/train.sh index 40bf72279..ef2b4b53c 100755 --- a/python/selfplay/train.sh +++ b/python/selfplay/train.sh @@ -72,7 +72,7 @@ else exit 1 fi -time python3 ./train.py \ +time python ./train.py \ -traindir "$BASEDIR"/train/"$TRAININGNAME" \ -latestdatadir "$BASEDIR"/shuffleddata/ \ -exportdir "$BASEDIR"/"$EXPORT_SUBDIR" \