CUDA
This commit is contained in:
+11
-1
@@ -34,9 +34,18 @@ Makefile
|
|||||||
*.tlog
|
*.tlog
|
||||||
*.lastbuildstate
|
*.lastbuildstate
|
||||||
|
|
||||||
# Generated searches, checkpoints, reports, and logs
|
# Machine-local runtime files
|
||||||
/runtime/
|
/runtime/
|
||||||
|
|
||||||
|
# Search checkpoints under results/search are intentionally tracked so that
|
||||||
|
# disjoint topology ranges merge through Git. These files are derived locally.
|
||||||
|
/results/search/leaderboard.tsv
|
||||||
|
/results/search/run.log
|
||||||
|
/results/search/runs/**/run.log
|
||||||
|
/results/search/**/*.tmp
|
||||||
|
/results/search/**/*.tmp.*
|
||||||
|
/results/search/**/*.partial
|
||||||
|
|
||||||
# Python cache and virtual environments
|
# Python cache and virtual environments
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
@@ -47,6 +56,7 @@ venv/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
Desktop.ini
|
Desktop.ini
|
||||||
|
*.lnk
|
||||||
|
|
||||||
# Wavefront .obj files are research data in this repository and are intentionally
|
# Wavefront .obj files are research data in this repository and are intentionally
|
||||||
# not ignored globally.
|
# not ignored globally.
|
||||||
|
|||||||
+30
-1
@@ -6,12 +6,16 @@ set(CMAKE_CXX_STANDARD 17)
|
|||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
option(SZILASSI_ENABLE_CUDA "Build the FP32 CUDA global-search backend" ON)
|
||||||
|
|
||||||
set(EIGEN_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/external/eigen-3.4.0" CACHE PATH "Eigen include root")
|
set(EIGEN_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/external/eigen-3.4.0" CACHE PATH "Eigen include root")
|
||||||
if(NOT EXISTS "${EIGEN_ROOT}/Eigen/Dense")
|
if(NOT EXISTS "${EIGEN_ROOT}/Eigen/Dense")
|
||||||
message(FATAL_ERROR "Eigen not found at ${EIGEN_ROOT}. Download Eigen 3.4.0 into external/eigen-3.4.0 or pass -DEIGEN_ROOT=...")
|
message(FATAL_ERROR "Eigen not found at ${EIGEN_ROOT}. Download Eigen 3.4.0 into external/eigen-3.4.0 or pass -DEIGEN_ROOT=...")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(neighborly_core
|
add_library(neighborly_core
|
||||||
|
src/Checkpoint/GlobalCheckpoint.cpp
|
||||||
|
src/Checkpoint/GlobalCheckpoint.h
|
||||||
src/NeighborlyCore/plane.h
|
src/NeighborlyCore/plane.h
|
||||||
src/NeighborlyCore/precise_geometry.h
|
src/NeighborlyCore/precise_geometry.h
|
||||||
src/NeighborlyCore/util.cpp
|
src/NeighborlyCore/util.cpp
|
||||||
@@ -21,9 +25,33 @@ add_library(neighborly_core
|
|||||||
projects/Szilassi/solver.h
|
projects/Szilassi/solver.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
include(CheckLanguage)
|
||||||
|
if(SZILASSI_ENABLE_CUDA)
|
||||||
|
check_language(CUDA)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SZILASSI_ENABLE_CUDA AND CMAKE_CUDA_COMPILER)
|
||||||
|
enable_language(CUDA)
|
||||||
|
add_library(neighborly_cuda src/CudaSearch/cuda_search.cu)
|
||||||
|
set_target_properties(neighborly_cuda PROPERTIES
|
||||||
|
CUDA_ARCHITECTURES "89-real;120-real;120-virtual"
|
||||||
|
CUDA_STANDARD 17
|
||||||
|
CUDA_STANDARD_REQUIRED ON
|
||||||
|
)
|
||||||
|
target_compile_definitions(neighborly_cuda PUBLIC SZILASSI_HAS_CUDA=1)
|
||||||
|
else()
|
||||||
|
add_library(neighborly_cuda src/CudaSearch/cuda_search_stub.cpp)
|
||||||
|
message(STATUS "CUDA Toolkit not found: building a diagnostic stub backend")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_include_directories(neighborly_cuda PUBLIC
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/CudaSearch"
|
||||||
|
)
|
||||||
|
|
||||||
target_include_directories(neighborly_core
|
target_include_directories(neighborly_core
|
||||||
PUBLIC
|
PUBLIC
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/NeighborlyCore"
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/NeighborlyCore"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/Checkpoint"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/projects/Szilassi"
|
"${CMAKE_CURRENT_SOURCE_DIR}/projects/Szilassi"
|
||||||
"${EIGEN_ROOT}"
|
"${EIGEN_ROOT}"
|
||||||
)
|
)
|
||||||
@@ -36,7 +64,7 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(neighborly_main projects/Szilassi/main.cpp)
|
add_executable(neighborly_main projects/Szilassi/main.cpp)
|
||||||
target_link_libraries(neighborly_main PRIVATE neighborly_core)
|
target_link_libraries(neighborly_main PRIVATE neighborly_core neighborly_cuda)
|
||||||
|
|
||||||
add_executable(verify_cpp projects/VerifyCpp/verify_cpp.cpp)
|
add_executable(verify_cpp projects/VerifyCpp/verify_cpp.cpp)
|
||||||
target_link_libraries(verify_cpp PRIVATE neighborly_core)
|
target_link_libraries(verify_cpp PRIVATE neighborly_core)
|
||||||
@@ -44,5 +72,6 @@ target_link_libraries(verify_cpp PRIVATE neighborly_core)
|
|||||||
add_executable(polyhedron_gui WIN32 projects/PolyhedronGui/launcher_gui.cpp)
|
add_executable(polyhedron_gui WIN32 projects/PolyhedronGui/launcher_gui.cpp)
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
target_compile_definitions(polyhedron_gui PRIVATE UNICODE _UNICODE NOMINMAX)
|
target_compile_definitions(polyhedron_gui PRIVATE UNICODE _UNICODE NOMINMAX)
|
||||||
|
target_compile_options(polyhedron_gui PRIVATE /utf-8)
|
||||||
target_link_libraries(polyhedron_gui PRIVATE shell32)
|
target_link_libraries(polyhedron_gui PRIVATE shell32)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -9,17 +9,58 @@ The conclusion is: Sort of. I believe there isn't a shape that is _completely_ i
|
|||||||
|
|
||||||
Build `Szilassi.slnx` as `Release|x64`, then open
|
Build `Szilassi.slnx` as `Release|x64`, then open
|
||||||
`build\msbuild\bin\x64\Release\PolyhedronGui.exe`.
|
`build\msbuild\bin\x64\Release\PolyhedronGui.exe`.
|
||||||
For an independent overnight run, leave `Threads` at `0`, set the desired number of
|
The GUI intentionally has only **Запустить поиск** and **Остановить поиск** buttons.
|
||||||
minutes, and click **Глобальный поиск 59**. This mode does not use the supplied near-miss:
|
Select an inclusive topology range, then start the search. Existing checkpoints are
|
||||||
|
loaded automatically. Stop waits for the current short CUDA kernel, synchronizes the
|
||||||
|
device, writes a durable checkpoint, and only then exits.
|
||||||
|
|
||||||
|
The GUI creates a fresh seed automatically for every new process. It is shown in the
|
||||||
|
GUI log and saved in the durable, Git-tracked `runs/<UUID>/run.tsv` manifest together
|
||||||
|
with the topology range and search parameters, so a run can be reproduced without
|
||||||
|
typing a seed into the interface.
|
||||||
|
|
||||||
|
The search process and CUDA stream run at low scheduling priority, so foreground work
|
||||||
|
keeps priority. There is no artificial duty-cycle throttle. Kernels remain short for
|
||||||
|
responsive stopping and Windows WDDM stability.
|
||||||
|
|
||||||
|
Leave `GPU chains` at `0` for automatic sizing. The CUDA backend derives the chain count
|
||||||
|
from the selected GPU's SM count and measured kernel occupancy, so Ada `sm_89` and
|
||||||
|
Blackwell `sm_120` use different appropriate values. Each selected topology keeps its
|
||||||
|
annealing session resident on the device, so refresh cycles cannot discard cooling,
|
||||||
|
stagnation, or restart state. The full 59-topology range uses about 1.4 GB of VRAM on
|
||||||
|
the 12 GB RTX 4070 Ti; narrower ranges allocate proportionally less.
|
||||||
|
|
||||||
|
For an independent overnight run, set the desired number of minutes and topology range.
|
||||||
|
This mode does not use the supplied near-miss:
|
||||||
it gives every topology fresh random starts, then alternates depth passes over the best
|
it gives every topology fresh random starts, then alternates depth passes over the best
|
||||||
30/16/8 topologies with periodic breadth refreshes over all 59. A clean stop saves each
|
30/16/8 topologies with periodic breadth refreshes over all 59. A clean stop saves each
|
||||||
topology under `runtime\global_search\topology_N`; pressing the same button later resumes only
|
topology under `results\search\topology_N`; pressing the same button later resumes only
|
||||||
those checkpoints. `runtime\global_search\leaderboard.tsv` is the current ranking.
|
those checkpoints. `results\search\leaderboard.tsv` is the current ranking.
|
||||||
|
|
||||||
Coordinates and optimizer states use 64-bit `double`. Candidates with at most 12 total
|
The CUDA hot path uses standard FP32 math. Persistent chains retain their RNG, cooling,
|
||||||
defects are automatically reclassified with the custom `WideReal` double-double type
|
stagnation, and restart state between batches; fixed fresh cohorts preserve global
|
||||||
(about 31 decimal digits), and every claimed `0/0` is serialized and checked again at
|
exploration during breadth/refresh passes. Every returned shortlist is reconstructed and checked on
|
||||||
multiple tolerances down to `1e-13`. OBJ and plane checkpoints retain 17 digits.
|
the CPU in `double`; candidates with at most 12 total defects are reclassified with the
|
||||||
|
custom `WideReal` double-double type (about 31 decimal digits). Every claimed `0/0` is
|
||||||
|
serialized and checked again at multiple tolerances down to `1e-13`.
|
||||||
|
|
||||||
|
CUDA Toolkit 13.3 with Visual Studio integration is required for the GPU backend. The
|
||||||
|
build contains native targets for Ada `sm_89` and Blackwell `sm_120`. Without the Toolkit,
|
||||||
|
the project builds a diagnostic stub and refuses `--cuda` instead of silently falling
|
||||||
|
back to the CPU.
|
||||||
|
|
||||||
|
### Checkpoints and Git
|
||||||
|
|
||||||
|
Search state is stored in `results/search`, which is intentionally tracked by Git.
|
||||||
|
Each checkpoint is an immutable, self-contained `.szcp` file with CRC-32 and exact FP32
|
||||||
|
bit patterns. It is flushed to disk and atomically published; a damaged newest generation
|
||||||
|
is ignored in favor of the previous valid one.
|
||||||
|
|
||||||
|
For several computers, assign non-overlapping topology ranges. Each process uses a unique
|
||||||
|
run UUID, so checkpoint filenames do not collide. Commit `results/search` normally on each
|
||||||
|
computer and merge the branches with Git. `leaderboard.tsv`, run logs, and temporary files
|
||||||
|
are derived and ignored, so they cannot create merge conflicts; the next search start
|
||||||
|
rescans checkpoints, repeats CPU/DD validation, and rebuilds the leaderboard.
|
||||||
|
|
||||||
## Project layout
|
## Project layout
|
||||||
|
|
||||||
@@ -29,7 +70,8 @@ multiple tolerances down to `1e-13`. OBJ and plane checkpoints retain 17 digits.
|
|||||||
* `src/NeighborlyCore` — shared geometry and utility code.
|
* `src/NeighborlyCore` — shared geometry and utility code.
|
||||||
* `data` — topology definitions and input models.
|
* `data` — topology definitions and input models.
|
||||||
* `results/topologies` and `results/showcases` — saved research models.
|
* `results/topologies` and `results/showcases` — saved research models.
|
||||||
* `runtime` — generated candidates, reports, logs, and resumable search state.
|
* `results/search` — Git-mergeable search states and durable checkpoint generations.
|
||||||
|
* `runtime` — temporary stop files, reports, logs, and local candidates.
|
||||||
* `scripts` — helper launch and reporting scripts.
|
* `scripts` — helper launch and reporting scripts.
|
||||||
|
|
||||||
All Visual Studio projects are collected by the root `Szilassi.slnx`. MSBuild output is
|
All Visual Studio projects are collected by the root `Szilassi.slnx`. MSBuild output is
|
||||||
@@ -39,7 +81,8 @@ centralized under `build/msbuild`; CMake output remains under `build/vs2026`.
|
|||||||
To start a search, build and run the `Szilassi` project.
|
To start a search, build and run the `Szilassi` project.
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
* C++17 compatible compiler (I'm using MSVC 2019).
|
* C++17 compatible compiler (Visual Studio 2026 is configured by the current presets).
|
||||||
|
* [CUDA Toolkit 13.3](https://developer.nvidia.com/cuda-downloads) with Visual Studio integration for GPU search.
|
||||||
* [Eigen-3.4.0](https://eigen.tuxfamily.org/) though similar versions should work as well.
|
* [Eigen-3.4.0](https://eigen.tuxfamily.org/) though similar versions should work as well.
|
||||||
* [Cairo-1.17.2](https://www.cairographics.org/) (optional) this is only needed to render paper cutouts.
|
* [Cairo-1.17.2](https://www.cairographics.org/) (optional) this is only needed to render paper cutouts.
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -53,7 +53,11 @@
|
|||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<CudaToolkitAvailable Condition="'$(Platform)'=='x64' and Exists('$(VCTargetsPath)\BuildCustomizations\CUDA 13.3.props')">true</CudaToolkitAvailable>
|
||||||
|
</PropertyGroup>
|
||||||
<ImportGroup Label="ExtensionSettings">
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
<Import Project="$(VCTargetsPath)\BuildCustomizations\CUDA 13.3.props" Condition="'$(CudaToolkitAvailable)'=='true'" />
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
<ImportGroup Label="Shared">
|
<ImportGroup Label="Shared">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
@@ -94,7 +98,7 @@
|
|||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\src\Checkpoint;$(ProjectDir)..\..\src\CudaSearch;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
@@ -111,7 +115,7 @@
|
|||||||
<SDLCheck>false</SDLCheck>
|
<SDLCheck>false</SDLCheck>
|
||||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\src\Checkpoint;$(ProjectDir)..\..\src\CudaSearch;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||||
<EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
|
<EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
|
||||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||||
@@ -132,7 +136,7 @@
|
|||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\src\Checkpoint;$(ProjectDir)..\..\src\CudaSearch;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
@@ -149,7 +153,7 @@
|
|||||||
<SDLCheck>false</SDLCheck>
|
<SDLCheck>false</SDLCheck>
|
||||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\src\Checkpoint;$(ProjectDir)..\..\src\CudaSearch;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||||
<EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
|
<EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
|
||||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||||
@@ -167,16 +171,28 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="main.cpp" />
|
<ClCompile Include="main.cpp" />
|
||||||
<ClCompile Include="solver.cpp" />
|
<ClCompile Include="solver.cpp" />
|
||||||
|
<ClCompile Include="..\..\src\Checkpoint\GlobalCheckpoint.cpp" />
|
||||||
<ClCompile Include="..\..\src\NeighborlyCore\util.cpp" />
|
<ClCompile Include="..\..\src\NeighborlyCore\util.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup Condition="'$(CudaToolkitAvailable)'!='true'">
|
||||||
|
<ClCompile Include="..\..\src\CudaSearch\cuda_search_stub.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup Condition="'$(CudaToolkitAvailable)'=='true'">
|
||||||
|
<CudaCompile Include="..\..\src\CudaSearch\cuda_search.cu">
|
||||||
|
<CodeGeneration>compute_89,sm_89;compute_120,sm_120;compute_120,compute_120</CodeGeneration>
|
||||||
|
</CudaCompile>
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\..\src\NeighborlyCore\plane.h" />
|
<ClInclude Include="..\..\src\NeighborlyCore\plane.h" />
|
||||||
<ClInclude Include="..\..\src\NeighborlyCore\precise_geometry.h" />
|
<ClInclude Include="..\..\src\NeighborlyCore\precise_geometry.h" />
|
||||||
<ClInclude Include="solver.h" />
|
<ClInclude Include="solver.h" />
|
||||||
|
<ClInclude Include="..\..\src\Checkpoint\GlobalCheckpoint.h" />
|
||||||
|
<ClInclude Include="..\..\src\CudaSearch\cuda_search.h" />
|
||||||
<ClInclude Include="..\..\src\NeighborlyCore\util.h" />
|
<ClInclude Include="..\..\src\NeighborlyCore\util.h" />
|
||||||
<ClInclude Include="..\..\src\NeighborlyCore\wide_real.h" />
|
<ClInclude Include="..\..\src\NeighborlyCore\wide_real.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="$(VCTargetsPath)\BuildCustomizations\CUDA 13.3.targets" Condition="'$(CudaToolkitAvailable)'=='true'" />
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -21,6 +21,15 @@
|
|||||||
<ClCompile Include="solver.cpp">
|
<ClCompile Include="solver.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\Checkpoint\GlobalCheckpoint.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\CudaSearch\cuda_search_stub.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<CudaCompile Include="..\..\src\CudaSearch\cuda_search.cu">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</CudaCompile>
|
||||||
<ClCompile Include="..\..\src\NeighborlyCore\util.cpp">
|
<ClCompile Include="..\..\src\NeighborlyCore\util.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -29,6 +38,12 @@
|
|||||||
<ClInclude Include="solver.h">
|
<ClInclude Include="solver.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\Checkpoint\GlobalCheckpoint.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\CudaSearch\cuda_search.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\NeighborlyCore\util.h">
|
<ClInclude Include="..\..\src\NeighborlyCore\util.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|||||||
+652
-48
@@ -2,6 +2,8 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "solver.h"
|
#include "solver.h"
|
||||||
#include "wide_real.h"
|
#include "wide_real.h"
|
||||||
|
#include "GlobalCheckpoint.h"
|
||||||
|
#include "cuda_search.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -18,16 +20,25 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <memory>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
#ifdef USE_CAIRO
|
#ifdef USE_CAIRO
|
||||||
#include <cairo.h>
|
#include <cairo.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define NUM_TOPOLOGIES 59
|
#define NUM_TOPOLOGIES 59
|
||||||
#define DUAL_PROBLEM 0
|
#define DUAL_PROBLEM 0
|
||||||
|
constexpr int GLOBAL_PLANE_VALUE_COUNT = 36;
|
||||||
|
constexpr int CUDA_SESSION_CACHE_LIMIT = NUM_TOPOLOGIES;
|
||||||
|
|
||||||
struct StudyOptions {
|
struct StudyOptions {
|
||||||
std::string obj_path = "data/shape_c2_i0_0.obj";
|
std::string obj_path = "data/shape_c2_i0_0.obj";
|
||||||
@@ -47,7 +58,7 @@ struct LocalRepairOptions {
|
|||||||
std::string obj_path = "data/shape_c2_i0_0.obj";
|
std::string obj_path = "data/shape_c2_i0_0.obj";
|
||||||
std::string start_planes_path;
|
std::string start_planes_path;
|
||||||
std::string stop_file_path;
|
std::string stop_file_path;
|
||||||
std::string global_dir = "runtime/global_search";
|
std::string global_dir = "results/search";
|
||||||
std::string out_prefix = "runtime/candidates/top4_local";
|
std::string out_prefix = "runtime/candidates/top4_local";
|
||||||
std::string report_path = "runtime/reports/03_repair_local_cpp.md";
|
std::string report_path = "runtime/reports/03_repair_local_cpp.md";
|
||||||
int topology = 4;
|
int topology = 4;
|
||||||
@@ -65,6 +76,13 @@ struct LocalRepairOptions {
|
|||||||
int time_limit_seconds = 0;
|
int time_limit_seconds = 0;
|
||||||
double jump_chance = 0.08;
|
double jump_chance = 0.08;
|
||||||
double min_step_ratio = 1e-5;
|
double min_step_ratio = 1e-5;
|
||||||
|
int topology_from = 0;
|
||||||
|
int topology_to = NUM_TOPOLOGIES - 1;
|
||||||
|
int cuda_chains = 0;
|
||||||
|
int cuda_iterations = 64;
|
||||||
|
int checkpoint_seconds = 30;
|
||||||
|
double degeneracy_weight = 0.02;
|
||||||
|
bool use_cuda = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool save_plane_state(const std::filesystem::path& path, const VectorXd& x);
|
bool save_plane_state(const std::filesystem::path& path, const VectorXd& x);
|
||||||
@@ -122,7 +140,14 @@ void print_usage(const char* exe_name) {
|
|||||||
<< " --threads <n> batch-hunt worker threads. 0 = all cores.\n"
|
<< " --threads <n> batch-hunt worker threads. 0 = all cores.\n"
|
||||||
<< " --minutes <x> Clean time limit for batch-hunt. 0 = unlimited.\n"
|
<< " --minutes <x> Clean time limit for batch-hunt. 0 = unlimited.\n"
|
||||||
<< " --stop-file <path> Stop cleanly when this file appears.\n"
|
<< " --stop-file <path> Stop cleanly when this file appears.\n"
|
||||||
<< " --global-dir <path> Checkpoints for --global-search. Default: runtime/global_search\n"
|
<< " --global-dir <path> Checkpoints for --global-search. Default: results/search\n"
|
||||||
|
<< " --topology-from <n> First topology to search, inclusive. Default: 0\n"
|
||||||
|
<< " --topology-to <n> Last topology to search, inclusive. Default: 58\n"
|
||||||
|
<< " --cuda Require the FP32 CUDA search backend.\n"
|
||||||
|
<< " --cuda-chains <n> Parallel GPU chains. 0 = automatic (default).\n"
|
||||||
|
<< " --cuda-iters <n> Iterations per short GPU batch. Default: 64\n"
|
||||||
|
<< " --checkpoint-seconds <n> Durable checkpoint period. Default: 30\n"
|
||||||
|
<< " --degeneracy-weight <x> Degenerate-geometry penalty. Default: 0.02\n"
|
||||||
<< " --start-planes <p> Continue batch-hunt from a saved .planes sidecar.\n"
|
<< " --start-planes <p> Continue batch-hunt from a saved .planes sidecar.\n"
|
||||||
<< " --restarts <n> hunt-local restarts. Default: 256\n"
|
<< " --restarts <n> hunt-local restarts. Default: 256\n"
|
||||||
<< " --stagnation <n> Iterations before hunt-local reheat. Default: 2000\n"
|
<< " --stagnation <n> Iterations before hunt-local reheat. Default: 2000\n"
|
||||||
@@ -2170,15 +2195,50 @@ struct GlobalMetrics {
|
|||||||
int intersections = std::numeric_limits<int>::max() / 4;
|
int intersections = std::numeric_limits<int>::max() / 4;
|
||||||
double crossing_loss = std::numeric_limits<double>::infinity();
|
double crossing_loss = std::numeric_limits<double>::infinity();
|
||||||
double geometry_penalty = std::numeric_limits<double>::infinity();
|
double geometry_penalty = std::numeric_limits<double>::infinity();
|
||||||
|
double degeneracy_penalty = std::numeric_limits<double>::infinity();
|
||||||
double energy = std::numeric_limits<double>::infinity();
|
double energy = std::numeric_limits<double>::infinity();
|
||||||
bool canonical = false;
|
bool canonical = false;
|
||||||
bool precise = false;
|
bool precise = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
double g_global_degeneracy_weight = 0.02;
|
||||||
|
std::string g_search_device_id = "CPU";
|
||||||
|
|
||||||
|
std::uint64_t topology_fingerprint() {
|
||||||
|
std::uint64_t hash = 1469598103934665603ULL;
|
||||||
|
auto add = [&](int value) {
|
||||||
|
hash ^= static_cast<std::uint64_t>(static_cast<std::uint32_t>(value));
|
||||||
|
hash *= 1099511628211ULL;
|
||||||
|
};
|
||||||
|
for (const Face& triangle : g_tris) {
|
||||||
|
add(static_cast<int>(triangle.size()));
|
||||||
|
for (int value : triangle) {
|
||||||
|
add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const Face& polygon : g_polys) {
|
||||||
|
add(static_cast<int>(polygon.size()));
|
||||||
|
for (int value : polygon) {
|
||||||
|
add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const Edge& edge : g_edges) {
|
||||||
|
add(edge.first);
|
||||||
|
add(edge.second);
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
struct GlobalTopologyState {
|
struct GlobalTopologyState {
|
||||||
int topology = 0;
|
int topology = 0;
|
||||||
int visits = 0;
|
std::uint64_t visits = 0;
|
||||||
std::uint64_t trials = 0;
|
std::uint64_t trials = 0;
|
||||||
|
std::uint64_t iterations = 0;
|
||||||
|
std::uint64_t run_visits = 0;
|
||||||
|
std::uint64_t run_trials = 0;
|
||||||
|
std::uint64_t run_iterations = 0;
|
||||||
|
std::uint64_t checkpoint_sequence = 0;
|
||||||
|
std::chrono::steady_clock::time_point last_checkpoint_at{};
|
||||||
bool has_state = false;
|
bool has_state = false;
|
||||||
GlobalMetrics best;
|
GlobalMetrics best;
|
||||||
VectorXd best_x;
|
VectorXd best_x;
|
||||||
@@ -2354,13 +2414,57 @@ GlobalMetrics evaluate_global_state(
|
|||||||
scratch.verts, *metric_planes, 1e-8, &metrics.crossing_loss);
|
scratch.verts, *metric_planes, 1e-8, &metrics.crossing_loss);
|
||||||
metrics.intersections = count_edge_face_intersections_strict(
|
metrics.intersections = count_edge_face_intersections_strict(
|
||||||
scratch.verts, *metric_planes, 1e-8);
|
scratch.verts, *metric_planes, 1e-8);
|
||||||
metrics.precise = promote_precise_counts_if_close(
|
metrics.precise = canonical && promote_precise_counts_if_close(
|
||||||
scratch.verts, *metric_planes, metrics.crossings, metrics.intersections);
|
scratch.verts, *metric_planes, metrics.crossings, metrics.intersections);
|
||||||
|
|
||||||
const double condition = std::sqrt(max_edge_sq / min_edge_sq);
|
const double condition = std::sqrt(max_edge_sq / min_edge_sq);
|
||||||
|
const double relative_min_edge = std::sqrt(min_edge_sq / max_edge_sq);
|
||||||
|
double min_plane_determinant = std::numeric_limits<double>::infinity();
|
||||||
|
for (const Face& triangle : g_tris) {
|
||||||
|
if (triangle.size() != 3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const Vector3d& a = (*metric_planes)[triangle[0]].n;
|
||||||
|
const Vector3d& b = (*metric_planes)[triangle[1]].n;
|
||||||
|
const Vector3d& c = (*metric_planes)[triangle[2]].n;
|
||||||
|
min_plane_determinant = std::min(
|
||||||
|
min_plane_determinant,
|
||||||
|
std::abs(a.dot(b.cross(c))));
|
||||||
|
}
|
||||||
|
if (!std::isfinite(min_plane_determinant)) {
|
||||||
|
return GlobalMetrics{};
|
||||||
|
}
|
||||||
|
double min_turn_sine = std::numeric_limits<double>::infinity();
|
||||||
|
for (const Face& face : g_polys) {
|
||||||
|
for (size_t i = 0; i < face.size(); ++i) {
|
||||||
|
const Vector3d a = scratch.verts[face[(i + face.size() - 1) % face.size()]] -
|
||||||
|
scratch.verts[face[i]];
|
||||||
|
const Vector3d b = scratch.verts[face[(i + 1) % face.size()]] -
|
||||||
|
scratch.verts[face[i]];
|
||||||
|
const double denominator = a.norm() * b.norm();
|
||||||
|
if (denominator > 1e-15) {
|
||||||
|
min_turn_sine = std::min(
|
||||||
|
min_turn_sine,
|
||||||
|
a.cross(b).norm() / denominator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!std::isfinite(min_turn_sine)) {
|
||||||
|
return GlobalMetrics{};
|
||||||
|
}
|
||||||
|
const double determinant_barrier =
|
||||||
|
std::log1p(0.02 / std::max(1e-10, min_plane_determinant));
|
||||||
|
const double edge_barrier =
|
||||||
|
std::log1p(0.002 / std::max(1e-10, relative_min_edge));
|
||||||
|
const double turn_barrier =
|
||||||
|
std::log1p(0.002 / std::max(1e-10, min_turn_sine));
|
||||||
|
const double extent_barrier = 0.10 * std::log1p(max_vertex_norm / 100.0);
|
||||||
|
metrics.degeneracy_penalty = g_global_degeneracy_weight *
|
||||||
|
(determinant_barrier + edge_barrier + turn_barrier + extent_barrier);
|
||||||
metrics.geometry_penalty =
|
metrics.geometry_penalty =
|
||||||
0.0010 * std::min(20.0, std::log1p(condition)) +
|
0.0010 * std::min(20.0, std::log1p(condition)) +
|
||||||
0.0002 * std::min(20.0, std::log1p(max_vertex_norm));
|
0.0002 * std::min(20.0, std::log1p(max_vertex_norm)) +
|
||||||
|
metrics.degeneracy_penalty;
|
||||||
metrics.energy =
|
metrics.energy =
|
||||||
0.05 * static_cast<double>(global_defects(metrics)) +
|
0.05 * static_cast<double>(global_defects(metrics)) +
|
||||||
0.01 * static_cast<double>(metrics.crossings) +
|
0.01 * static_cast<double>(metrics.crossings) +
|
||||||
@@ -2529,11 +2633,131 @@ bool load_global_topology_state(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void load_mergeable_checkpoints(
|
||||||
|
const LocalRepairOptions& options,
|
||||||
|
std::vector<GlobalTopologyState>& states
|
||||||
|
) {
|
||||||
|
using namespace szilassi::checkpoint;
|
||||||
|
const CheckpointScan scan = scan_checkpoints(options.global_dir);
|
||||||
|
const LatestCheckpointMap latest = select_latest_per_run_topology(scan);
|
||||||
|
for (const auto& entry : latest) {
|
||||||
|
const GlobalCheckpoint& checkpoint = entry.second.checkpoint;
|
||||||
|
if (checkpoint.topology < 0 || checkpoint.topology >= NUM_TOPOLOGIES ||
|
||||||
|
checkpoint.plane_coefficients.size() != static_cast<size_t>(GLOBAL_PLANE_VALUE_COUNT)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!load_global_topology_context(checkpoint.topology)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (checkpoint.topology_fingerprint != 0 &&
|
||||||
|
checkpoint.topology_fingerprint != topology_fingerprint()) {
|
||||||
|
std::cerr << "Skipping checkpoint with a different topology fingerprint: "
|
||||||
|
<< entry.second.path.string() << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
VectorXd x(GLOBAL_PLANE_VALUE_COUNT);
|
||||||
|
for (int i = 0; i < GLOBAL_PLANE_VALUE_COUNT; ++i) {
|
||||||
|
x[i] = static_cast<double>(checkpoint.plane_coefficients[static_cast<size_t>(i)]);
|
||||||
|
}
|
||||||
|
if (!valid_plane_state(x)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PlaneEvaluationScratch scratch;
|
||||||
|
GlobalMetrics metrics = evaluate_global_state(x, true, scratch);
|
||||||
|
if (!std::isfinite(metrics.energy)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GlobalTopologyState& state = states[checkpoint.topology];
|
||||||
|
state.visits += checkpoint.visits;
|
||||||
|
state.trials += checkpoint.completed_trials;
|
||||||
|
state.iterations += checkpoint.completed_iterations;
|
||||||
|
if (!state.has_state || better_global_metrics(metrics, state.best)) {
|
||||||
|
state.best = metrics;
|
||||||
|
state.best_x = x;
|
||||||
|
state.has_state = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!scan.rejected.empty()) {
|
||||||
|
std::cerr << "Ignored " << scan.rejected.size()
|
||||||
|
<< " incomplete or corrupt checkpoint(s); older generations remain usable."
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
if (!scan.conflicts.empty()) {
|
||||||
|
std::cerr << "Ignored " << scan.conflicts.size()
|
||||||
|
<< " conflicting checkpoint sequence(s)." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool commit_mergeable_checkpoint(
|
||||||
|
const LocalRepairOptions& options,
|
||||||
|
const szilassi::checkpoint::RunIdentity& identity,
|
||||||
|
GlobalTopologyState& state,
|
||||||
|
szilassi::checkpoint::CheckpointReason reason
|
||||||
|
) {
|
||||||
|
using namespace szilassi::checkpoint;
|
||||||
|
if (!state.has_state || state.best_x.size() != GLOBAL_PLANE_VALUE_COUNT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GlobalCheckpoint checkpoint;
|
||||||
|
checkpoint.run_id = identity.run_id;
|
||||||
|
checkpoint.node_id = identity.node_id;
|
||||||
|
checkpoint.producer_id = "Szilassi CUDA global search";
|
||||||
|
checkpoint.device_id = g_search_device_id;
|
||||||
|
checkpoint.sequence = ++state.checkpoint_sequence;
|
||||||
|
checkpoint.topology = state.topology;
|
||||||
|
checkpoint.topology_first = options.topology_from;
|
||||||
|
checkpoint.topology_last = options.topology_to;
|
||||||
|
checkpoint.objective_version = 2;
|
||||||
|
checkpoint.topology_fingerprint = topology_fingerprint();
|
||||||
|
checkpoint.base_seed = static_cast<std::uint64_t>(
|
||||||
|
static_cast<std::uint32_t>(options.seed));
|
||||||
|
checkpoint.next_work_unit = state.run_visits + 1;
|
||||||
|
checkpoint.completed_work_units = state.run_visits;
|
||||||
|
checkpoint.completed_trials = state.run_trials;
|
||||||
|
checkpoint.completed_iterations = state.run_iterations;
|
||||||
|
checkpoint.visits = state.run_visits;
|
||||||
|
checkpoint.plane_coefficients.resize(GLOBAL_PLANE_VALUE_COUNT);
|
||||||
|
VectorXd stored_x(GLOBAL_PLANE_VALUE_COUNT);
|
||||||
|
for (int i = 0; i < GLOBAL_PLANE_VALUE_COUNT; ++i) {
|
||||||
|
const float value = static_cast<float>(state.best_x[i]);
|
||||||
|
checkpoint.plane_coefficients[static_cast<size_t>(i)] = value;
|
||||||
|
stored_x[i] = static_cast<double>(value);
|
||||||
|
}
|
||||||
|
PlaneEvaluationScratch scratch;
|
||||||
|
GlobalMetrics stored_metrics = evaluate_global_state(stored_x, true, scratch);
|
||||||
|
if (!std::isfinite(stored_metrics.energy)) {
|
||||||
|
std::cerr << "FP32 checkpoint round-trip is invalid for topology "
|
||||||
|
<< state.topology << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
checkpoint.crossings = stored_metrics.crossings;
|
||||||
|
checkpoint.intersections = stored_metrics.intersections;
|
||||||
|
checkpoint.crossing_loss = stored_metrics.crossing_loss;
|
||||||
|
checkpoint.degeneracy_penalty = stored_metrics.degeneracy_penalty;
|
||||||
|
checkpoint.energy = stored_metrics.energy;
|
||||||
|
checkpoint.verification = stored_metrics.precise
|
||||||
|
? VerificationPrecision::DoubleDouble
|
||||||
|
: VerificationPrecision::Double;
|
||||||
|
checkpoint.reason = reason;
|
||||||
|
checkpoint.flags = CheckpointFlagCanonical |
|
||||||
|
(stored_metrics.precise ? CheckpointFlagDdVerified : CheckpointFlagNone) |
|
||||||
|
((stored_metrics.crossings == 0 && stored_metrics.intersections == 0)
|
||||||
|
? CheckpointFlagFound
|
||||||
|
: CheckpointFlagNone);
|
||||||
|
const CommitResult result = commit_checkpoint(options.global_dir, std::move(checkpoint));
|
||||||
|
if (!result) {
|
||||||
|
std::cerr << "Durable checkpoint failed for topology " << state.topology
|
||||||
|
<< ": " << result.error << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void save_global_topology_state(
|
void save_global_topology_state(
|
||||||
const LocalRepairOptions& options,
|
const LocalRepairOptions& options,
|
||||||
const GlobalTopologyState& state,
|
const GlobalTopologyState& state,
|
||||||
bool save_history,
|
bool,
|
||||||
int seed
|
int
|
||||||
) {
|
) {
|
||||||
if (!state.has_state || state.best_x.size() == 0) {
|
if (!state.has_state || state.best_x.size() == 0) {
|
||||||
return;
|
return;
|
||||||
@@ -2542,23 +2766,6 @@ void save_global_topology_state(
|
|||||||
std::filesystem::create_directories(dir);
|
std::filesystem::create_directories(dir);
|
||||||
save_plane_state(dir / "resume.planes", state.best_x);
|
save_plane_state(dir / "resume.planes", state.best_x);
|
||||||
export_plane_candidate((dir / "resume.obj").string().c_str(), state.best_x);
|
export_plane_candidate((dir / "resume.obj").string().c_str(), state.best_x);
|
||||||
std::ofstream meta(dir / "resume.meta");
|
|
||||||
meta << 1 << " " << state.visits << " " << state.trials << "\n";
|
|
||||||
meta << state.best.crossings << " " << state.best.intersections << " "
|
|
||||||
<< std::setprecision(17) << state.best.energy << "\n";
|
|
||||||
|
|
||||||
if (save_history) {
|
|
||||||
std::ostringstream stem;
|
|
||||||
stem << "best_v" << state.visits
|
|
||||||
<< "_strict_c" << state.best.crossings
|
|
||||||
<< "_i" << state.best.intersections
|
|
||||||
<< "_seed" << seed;
|
|
||||||
const std::filesystem::path obj_path = dir / (stem.str() + ".obj");
|
|
||||||
export_plane_candidate(obj_path.string().c_str(), state.best_x);
|
|
||||||
std::filesystem::path planes_path = obj_path;
|
|
||||||
planes_path.replace_extension(".planes");
|
|
||||||
save_plane_state(planes_path, state.best_x);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_global_leaderboard(
|
void write_global_leaderboard(
|
||||||
@@ -2580,7 +2787,7 @@ void write_global_leaderboard(
|
|||||||
const std::filesystem::path root(options.global_dir);
|
const std::filesystem::path root(options.global_dir);
|
||||||
std::filesystem::create_directories(root);
|
std::filesystem::create_directories(root);
|
||||||
std::ofstream out(root / "leaderboard.tsv");
|
std::ofstream out(root / "leaderboard.tsv");
|
||||||
out << "rank\ttopology\tC\tI\tdefects\tprecision\tvisits\ttrials\tenergy\n";
|
out << "rank\ttopology\tC\tI\tdefects\tprecision\tvisits\ttrials\tenergy\tdegeneracy\n";
|
||||||
int rank = 1;
|
int rank = 1;
|
||||||
for (int ix : order) {
|
for (int ix : order) {
|
||||||
const GlobalTopologyState& state = states[ix];
|
const GlobalTopologyState& state = states[ix];
|
||||||
@@ -2590,22 +2797,254 @@ void write_global_leaderboard(
|
|||||||
<< global_defects(state.best) << "\t"
|
<< global_defects(state.best) << "\t"
|
||||||
<< (state.best.precise ? "dd31" : "double") << "\t"
|
<< (state.best.precise ? "dd31" : "double") << "\t"
|
||||||
<< state.visits << "\t"
|
<< state.visits << "\t"
|
||||||
<< state.trials << "\t" << std::setprecision(17) << state.best.energy;
|
<< state.trials << "\t" << std::setprecision(17) << state.best.energy << "\t"
|
||||||
|
<< state.best.degeneracy_penalty;
|
||||||
} else {
|
} else {
|
||||||
out << "-\t-\t-\t-\t0\t0\t-";
|
out << "-\t-\t-\t-\t0\t0\t-\t-";
|
||||||
}
|
}
|
||||||
out << "\n";
|
out << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool write_run_manifest(
|
||||||
|
const std::filesystem::path& run_directory,
|
||||||
|
const LocalRepairOptions& options,
|
||||||
|
const szilassi::checkpoint::RunIdentity& identity,
|
||||||
|
int effective_cuda_chains,
|
||||||
|
int effective_cuda_session_cache
|
||||||
|
) {
|
||||||
|
const std::filesystem::path manifest_path = run_directory / "run.tsv";
|
||||||
|
std::ofstream out(manifest_path, std::ios::binary | std::ios::trunc);
|
||||||
|
if (!out) {
|
||||||
|
std::cerr << "Cannot write run manifest: " << manifest_path << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out << "format\tszilassi-global-search-v2\n"
|
||||||
|
<< "run_id\t" << identity.run_id << "\n"
|
||||||
|
<< "node_id\t" << identity.node_id << "\n"
|
||||||
|
<< "seed\t" << options.seed << "\n"
|
||||||
|
<< "topology_from\t" << options.topology_from << "\n"
|
||||||
|
<< "topology_to\t" << options.topology_to << "\n"
|
||||||
|
<< "backend\t" << (options.use_cuda ? "cuda-fp32" : "cpu-double") << "\n"
|
||||||
|
<< "cuda_math\tstandard-fp32\n"
|
||||||
|
<< "cuda_chains_requested\t" << options.cuda_chains << "\n"
|
||||||
|
<< "cuda_chains_effective\t" << effective_cuda_chains << "\n"
|
||||||
|
<< "cuda_iterations_per_batch\t" << options.cuda_iterations << "\n"
|
||||||
|
<< "cuda_depth_batches\t6\n"
|
||||||
|
<< "cuda_session_cache\t" << effective_cuda_session_cache << "\n"
|
||||||
|
<< "cpu_iterations_per_trial\t" << options.iterations << "\n"
|
||||||
|
<< "degeneracy_weight\t" << std::setprecision(17)
|
||||||
|
<< options.degeneracy_weight << "\n";
|
||||||
|
out.flush();
|
||||||
|
if (!out) {
|
||||||
|
std::cerr << "Cannot flush run manifest: " << manifest_path << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
const HANDLE handle = CreateFileW(
|
||||||
|
manifest_path.c_str(),
|
||||||
|
GENERIC_READ,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
nullptr,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
nullptr);
|
||||||
|
if (handle == INVALID_HANDLE_VALUE || !FlushFileBuffers(handle)) {
|
||||||
|
std::cerr << "Cannot durably flush run manifest: " << manifest_path << std::endl;
|
||||||
|
if (handle != INVALID_HANDLE_VALUE) {
|
||||||
|
CloseHandle(handle);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CloseHandle(handle);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
cuda_search::Topology make_cuda_topology() {
|
||||||
|
cuda_search::Topology topology;
|
||||||
|
for (int vertex = 0; vertex < cuda_search::kVertexCount; ++vertex) {
|
||||||
|
for (int component = 0; component < 3; ++component) {
|
||||||
|
topology.vertex_planes[vertex][component] = static_cast<std::uint8_t>(
|
||||||
|
g_tris[static_cast<size_t>(vertex)][static_cast<size_t>(component)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int face = 0; face < cuda_search::kPolygonCount; ++face) {
|
||||||
|
for (int vertex = 0; vertex < cuda_search::kPolygonVertexCount; ++vertex) {
|
||||||
|
topology.polygons[face][vertex] = static_cast<std::uint8_t>(
|
||||||
|
g_polys[static_cast<size_t>(face)][static_cast<size_t>(vertex)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int edge = 0; edge < cuda_search::kEdgeCount; ++edge) {
|
||||||
|
topology.edges[edge][0] = static_cast<std::uint8_t>(g_edges[edge].first);
|
||||||
|
topology.edges[edge][1] = static_cast<std::uint8_t>(g_edges[edge].second);
|
||||||
|
}
|
||||||
|
return topology;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CudaSessionSlot {
|
||||||
|
int topology = -1;
|
||||||
|
std::uint64_t last_used = 0;
|
||||||
|
cuda_search::BatchSession session;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool run_global_topology_round_cuda(
|
||||||
|
const LocalRepairOptions& options,
|
||||||
|
GlobalTopologyState& state,
|
||||||
|
bool depth,
|
||||||
|
const std::atomic<bool>& stop_requested,
|
||||||
|
int round_seed,
|
||||||
|
int effective_chain_count,
|
||||||
|
cuda_search::BatchSession& session
|
||||||
|
) {
|
||||||
|
const GlobalMetrics previous_best = state.best;
|
||||||
|
const bool previous_has_state = state.has_state;
|
||||||
|
const bool session_was_initialized = session.initialized();
|
||||||
|
if (!session_was_initialized) {
|
||||||
|
cuda_search::SearchConfig config;
|
||||||
|
config.chain_count = effective_chain_count;
|
||||||
|
config.iterations_per_kernel = std::clamp(options.cuda_iterations, 1, 16);
|
||||||
|
config.iterations_per_batch = options.cuda_iterations;
|
||||||
|
config.shortlist_size = std::min(128, config.chain_count);
|
||||||
|
config.seed = static_cast<std::uint64_t>(static_cast<std::uint32_t>(round_seed));
|
||||||
|
config.initial_step = static_cast<float>(options.step);
|
||||||
|
config.minimum_step = static_cast<float>(
|
||||||
|
std::max(1e-7, options.step * options.min_step_ratio));
|
||||||
|
config.cooling = static_cast<float>(options.beta);
|
||||||
|
config.initial_temperature = static_cast<float>(options.temperature);
|
||||||
|
config.minimum_temperature = static_cast<float>(options.temperature * 0.04);
|
||||||
|
config.jump_chance = static_cast<float>(options.jump_chance);
|
||||||
|
config.initial_state_jitter = static_cast<float>(options.step * 0.10);
|
||||||
|
config.stagnation_iterations = std::max(256, options.stagnation);
|
||||||
|
config.degeneracy_weight = static_cast<float>(options.degeneracy_weight);
|
||||||
|
|
||||||
|
std::vector<cuda_search::PlaneState> initial_states;
|
||||||
|
if (state.has_state && state.best_x.size() == GLOBAL_PLANE_VALUE_COUNT) {
|
||||||
|
cuda_search::PlaneState initial;
|
||||||
|
for (int i = 0; i < GLOBAL_PLANE_VALUE_COUNT; ++i) {
|
||||||
|
initial.values[static_cast<size_t>(i)] =
|
||||||
|
static_cast<float>(state.best_x[i]);
|
||||||
|
}
|
||||||
|
initial_states.push_back(initial);
|
||||||
|
}
|
||||||
|
std::string initialize_error;
|
||||||
|
if (!session.initialize(
|
||||||
|
make_cuda_topology(), config, initial_states, initialize_error)) {
|
||||||
|
std::cerr << "CUDA session initialization failed for topology "
|
||||||
|
<< state.topology << ": " << initialize_error << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int requested_batches = depth ? 6 : 1;
|
||||||
|
int completed_batches = 0;
|
||||||
|
double kernel_milliseconds = 0.0;
|
||||||
|
double transfer_milliseconds = 0.0;
|
||||||
|
std::uint64_t evaluated_states = 0;
|
||||||
|
std::uint64_t new_trials = session_was_initialized
|
||||||
|
? 0
|
||||||
|
: static_cast<std::uint64_t>(effective_chain_count);
|
||||||
|
std::string device_name;
|
||||||
|
PlaneEvaluationScratch scratch;
|
||||||
|
for (int batch = 0; batch < requested_batches; ++batch) {
|
||||||
|
if (stop_requested.load(std::memory_order_relaxed)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cuda_search::BatchRunConfig run_config;
|
||||||
|
run_config.step_scale = depth ? 1.0f : 1.5f;
|
||||||
|
if (batch == 0 && (previous_has_state || session_was_initialized)) {
|
||||||
|
run_config.fresh_numerator = depth ? 1 : 7;
|
||||||
|
run_config.fresh_denominator = depth ? 4 : 8;
|
||||||
|
if (session_was_initialized) {
|
||||||
|
const std::uint64_t chain_count =
|
||||||
|
static_cast<std::uint64_t>(effective_chain_count);
|
||||||
|
const std::uint64_t numerator =
|
||||||
|
static_cast<std::uint64_t>(run_config.fresh_numerator);
|
||||||
|
const std::uint64_t denominator =
|
||||||
|
static_cast<std::uint64_t>(run_config.fresh_denominator);
|
||||||
|
new_trials += (chain_count / denominator) * numerator +
|
||||||
|
std::min(chain_count % denominator, numerator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const cuda_search::BatchResult gpu = session.run(run_config);
|
||||||
|
if (!gpu.success) {
|
||||||
|
std::cerr << "CUDA batch failed for topology " << state.topology
|
||||||
|
<< ": " << gpu.error << std::endl;
|
||||||
|
session.reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
device_name = gpu.device_name;
|
||||||
|
g_search_device_id = gpu.device_name;
|
||||||
|
kernel_milliseconds += gpu.kernel_milliseconds;
|
||||||
|
transfer_milliseconds += gpu.transfer_milliseconds;
|
||||||
|
evaluated_states += gpu.evaluated_states;
|
||||||
|
completed_batches += 1;
|
||||||
|
|
||||||
|
for (const cuda_search::Candidate& candidate : gpu.shortlist) {
|
||||||
|
VectorXd x(GLOBAL_PLANE_VALUE_COUNT);
|
||||||
|
for (int i = 0; i < GLOBAL_PLANE_VALUE_COUNT; ++i) {
|
||||||
|
x[i] = static_cast<double>(
|
||||||
|
candidate.state.values[static_cast<size_t>(i)]);
|
||||||
|
}
|
||||||
|
if (!valid_plane_state(x)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GlobalMetrics verified = evaluate_global_state(x, true, scratch);
|
||||||
|
if (!std::isfinite(verified.energy)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!state.has_state || better_global_metrics(verified, state.best)) {
|
||||||
|
state.best = verified;
|
||||||
|
state.best_x = x;
|
||||||
|
state.has_state = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (completed_batches == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.visits += 1;
|
||||||
|
state.run_visits += 1;
|
||||||
|
state.trials += new_trials;
|
||||||
|
state.run_trials += new_trials;
|
||||||
|
state.iterations += evaluated_states;
|
||||||
|
state.run_iterations += evaluated_states;
|
||||||
|
std::cout << " CUDA " << device_name
|
||||||
|
<< ": " << std::fixed << std::setprecision(1)
|
||||||
|
<< kernel_milliseconds << " ms kernel, "
|
||||||
|
<< transfer_milliseconds << " ms transfer, "
|
||||||
|
<< evaluated_states << " FP32 states in "
|
||||||
|
<< completed_batches << " batch(es)"
|
||||||
|
<< std::defaultfloat << std::setprecision(6) << std::endl;
|
||||||
|
return state.has_state &&
|
||||||
|
(!previous_has_state || better_global_metrics(state.best, previous_best));
|
||||||
|
}
|
||||||
|
|
||||||
bool run_global_topology_round(
|
bool run_global_topology_round(
|
||||||
const LocalRepairOptions& options,
|
const LocalRepairOptions& options,
|
||||||
GlobalTopologyState& state,
|
GlobalTopologyState& state,
|
||||||
bool depth,
|
bool depth,
|
||||||
int worker_count,
|
int worker_count,
|
||||||
const std::atomic<bool>& stop_requested,
|
const std::atomic<bool>& stop_requested,
|
||||||
int round_seed
|
int round_seed,
|
||||||
|
int effective_cuda_chains,
|
||||||
|
cuda_search::BatchSession* cuda_session
|
||||||
) {
|
) {
|
||||||
|
if (options.use_cuda) {
|
||||||
|
if (cuda_session == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return run_global_topology_round_cuda(
|
||||||
|
options,
|
||||||
|
state,
|
||||||
|
depth,
|
||||||
|
stop_requested,
|
||||||
|
round_seed,
|
||||||
|
effective_cuda_chains,
|
||||||
|
*cuda_session);
|
||||||
|
}
|
||||||
const GlobalMetrics previous_best = state.best;
|
const GlobalMetrics previous_best = state.best;
|
||||||
const bool previous_has_state = state.has_state;
|
const bool previous_has_state = state.has_state;
|
||||||
GlobalMetrics shared_best = state.best;
|
GlobalMetrics shared_best = state.best;
|
||||||
@@ -2620,6 +3059,7 @@ bool run_global_topology_round(
|
|||||||
: std::max(1000, options.iterations / 3);
|
: std::max(1000, options.iterations / 3);
|
||||||
std::atomic<int> next_trial{0};
|
std::atomic<int> next_trial{0};
|
||||||
std::atomic<int> completed{0};
|
std::atomic<int> completed{0};
|
||||||
|
std::atomic<std::uint64_t> completed_iterations{0};
|
||||||
std::mutex best_mutex;
|
std::mutex best_mutex;
|
||||||
std::vector<std::thread> workers;
|
std::vector<std::thread> workers;
|
||||||
workers.reserve(worker_count);
|
workers.reserve(worker_count);
|
||||||
@@ -2656,6 +3096,9 @@ bool run_global_topology_round(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
completed.fetch_add(1, std::memory_order_relaxed);
|
completed.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
completed_iterations.fetch_add(
|
||||||
|
static_cast<std::uint64_t>(result.iterations),
|
||||||
|
std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2668,7 +3111,15 @@ bool run_global_topology_round(
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.visits += 1;
|
state.visits += 1;
|
||||||
state.trials += static_cast<std::uint64_t>(completed.load(std::memory_order_relaxed));
|
state.run_visits += 1;
|
||||||
|
const std::uint64_t round_trials =
|
||||||
|
static_cast<std::uint64_t>(completed.load(std::memory_order_relaxed));
|
||||||
|
const std::uint64_t round_iterations =
|
||||||
|
completed_iterations.load(std::memory_order_relaxed);
|
||||||
|
state.trials += round_trials;
|
||||||
|
state.iterations += round_iterations;
|
||||||
|
state.run_trials += round_trials;
|
||||||
|
state.run_iterations += round_iterations;
|
||||||
state.has_state = shared_has_state;
|
state.has_state = shared_has_state;
|
||||||
state.best = shared_best;
|
state.best = shared_best;
|
||||||
state.best_x = shared_best_x;
|
state.best_x = shared_best_x;
|
||||||
@@ -2683,9 +3134,38 @@ int global_search_all(const LocalRepairOptions& options) {
|
|||||||
}
|
}
|
||||||
const std::filesystem::path root(options.global_dir);
|
const std::filesystem::path root(options.global_dir);
|
||||||
std::filesystem::create_directories(root);
|
std::filesystem::create_directories(root);
|
||||||
if (!options.stop_file_path.empty()) {
|
const szilassi::checkpoint::RunIdentity run_identity =
|
||||||
std::error_code remove_error;
|
szilassi::checkpoint::make_run_identity();
|
||||||
std::filesystem::remove(options.stop_file_path, remove_error);
|
g_global_degeneracy_weight = options.degeneracy_weight;
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS);
|
||||||
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
cuda_search::BackendInfo cuda_backend;
|
||||||
|
int effective_cuda_chains = 0;
|
||||||
|
if (options.use_cuda) {
|
||||||
|
cuda_backend = cuda_search::query_backend(0);
|
||||||
|
if (!cuda_backend.available) {
|
||||||
|
std::cerr << "CUDA backend is unavailable: " << cuda_backend.error << std::endl;
|
||||||
|
std::cerr << "Install CUDA Toolkit 13.3 with Visual Studio integration, then rebuild x64."
|
||||||
|
<< std::endl;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
effective_cuda_chains = options.cuda_chains > 0
|
||||||
|
? options.cuda_chains
|
||||||
|
: cuda_backend.recommended_chain_count;
|
||||||
|
if (effective_cuda_chains <= 0) {
|
||||||
|
std::cerr << "CUDA backend did not provide a valid chain count." << std::endl;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
g_search_device_id = cuda_backend.device_name;
|
||||||
|
std::cout << "CUDA device: " << cuda_backend.device_name << std::endl;
|
||||||
|
std::cout << "CUDA SMs: " << cuda_backend.multiprocessor_count
|
||||||
|
<< ", resident blocks/SM: "
|
||||||
|
<< cuda_backend.active_blocks_per_multiprocessor << std::endl;
|
||||||
|
std::cout << "GPU scheduling: low-priority stream, no artificial throttle"
|
||||||
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned int hardware_threads = std::thread::hardware_concurrency();
|
const unsigned int hardware_threads = std::thread::hardware_concurrency();
|
||||||
@@ -2720,24 +3200,60 @@ int global_search_all(const LocalRepairOptions& options) {
|
|||||||
load_global_topology_state(options, states[topology]);
|
load_global_topology_state(options, states[topology]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
load_mergeable_checkpoints(options, states);
|
||||||
write_global_leaderboard(options, states);
|
write_global_leaderboard(options, states);
|
||||||
|
|
||||||
std::ofstream run_log(root / "run.log", std::ios::app);
|
std::vector<int> active_topologies;
|
||||||
|
active_topologies.reserve(options.topology_to - options.topology_from + 1);
|
||||||
|
for (int topology = options.topology_from; topology <= options.topology_to; ++topology) {
|
||||||
|
active_topologies.push_back(topology);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::filesystem::path run_directory =
|
||||||
|
root / "runs" / run_identity.run_id;
|
||||||
|
std::filesystem::create_directories(run_directory);
|
||||||
|
const int effective_cuda_session_cache = options.use_cuda
|
||||||
|
? std::min(
|
||||||
|
CUDA_SESSION_CACHE_LIMIT,
|
||||||
|
static_cast<int>(active_topologies.size()))
|
||||||
|
: 0;
|
||||||
|
write_run_manifest(
|
||||||
|
run_directory,
|
||||||
|
options,
|
||||||
|
run_identity,
|
||||||
|
effective_cuda_chains,
|
||||||
|
effective_cuda_session_cache);
|
||||||
|
std::ofstream run_log(run_directory / "run.log", std::ios::app);
|
||||||
run_log << "\n=== global-search seed " << options.seed
|
run_log << "\n=== global-search seed " << options.seed
|
||||||
|
<< ", run " << run_identity.run_id
|
||||||
|
<< ", node " << run_identity.node_id
|
||||||
<< ", threads " << worker_count
|
<< ", threads " << worker_count
|
||||||
<< ", minutes " << (options.time_limit_seconds / 60.0)
|
<< ", minutes " << (options.time_limit_seconds / 60.0)
|
||||||
<< " ===\n";
|
<< " ===\n";
|
||||||
|
|
||||||
std::cout << "===================" << std::endl;
|
std::cout << "===================" << std::endl;
|
||||||
std::cout << "Mode : global-search" << std::endl;
|
std::cout << "Mode : global-search" << std::endl;
|
||||||
std::cout << "Topologies: 59" << std::endl;
|
std::cout << "Seed : " << options.seed
|
||||||
|
<< " (saved in " << (run_directory / "run.tsv").string() << ")"
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << "Topologies: " << options.topology_from << ".."
|
||||||
|
<< options.topology_to << " (" << active_topologies.size() << ")" << std::endl;
|
||||||
std::cout << "Start : independent random plane arrangements" << std::endl;
|
std::cout << "Start : independent random plane arrangements" << std::endl;
|
||||||
std::cout << "Coordinates: double (15-16 digits)" << std::endl;
|
std::cout << "Coordinates: "
|
||||||
|
<< (options.use_cuda ? "CUDA FP32 search" : "CPU double search")
|
||||||
|
<< std::endl;
|
||||||
std::cout << "Near goal : WideReal double-double (~"
|
std::cout << "Near goal : WideReal double-double (~"
|
||||||
<< WideReal::decimal_digits << " digits)" << std::endl;
|
<< WideReal::decimal_digits << " digits)" << std::endl;
|
||||||
std::cout << "Resume dir: " << options.global_dir << std::endl;
|
std::cout << "Resume dir: " << options.global_dir << std::endl;
|
||||||
std::cout << "Threads : " << worker_count << std::endl;
|
if (options.use_cuda) {
|
||||||
std::cout << "Depth iters/trial: " << options.iterations << std::endl;
|
std::cout << "CUDA chains: " << effective_cuda_chains
|
||||||
|
<< (options.cuda_chains == 0 ? " (automatic)" : " (manual)")
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << "CUDA iters/batch: " << options.cuda_iterations << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "Threads : " << worker_count << std::endl;
|
||||||
|
std::cout << "Depth iters/trial: " << options.iterations << std::endl;
|
||||||
|
}
|
||||||
if (options.time_limit_seconds > 0) {
|
if (options.time_limit_seconds > 0) {
|
||||||
std::cout << "Time limit: " << options.time_limit_seconds << " sec" << std::endl;
|
std::cout << "Time limit: " << options.time_limit_seconds << " sec" << std::endl;
|
||||||
}
|
}
|
||||||
@@ -2745,6 +3261,34 @@ int global_search_all(const LocalRepairOptions& options) {
|
|||||||
|
|
||||||
int completed_rounds = 0;
|
int completed_rounds = 0;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
std::vector<CudaSessionSlot> cuda_sessions(effective_cuda_session_cache);
|
||||||
|
std::uint64_t cuda_session_use_counter = 0;
|
||||||
|
auto acquire_cuda_session = [&](int topology) -> cuda_search::BatchSession* {
|
||||||
|
if (!options.use_cuda || cuda_sessions.empty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
++cuda_session_use_counter;
|
||||||
|
for (CudaSessionSlot& slot : cuda_sessions) {
|
||||||
|
if (slot.topology == topology) {
|
||||||
|
slot.last_used = cuda_session_use_counter;
|
||||||
|
return &slot.session;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CudaSessionSlot* selected = nullptr;
|
||||||
|
for (CudaSessionSlot& slot : cuda_sessions) {
|
||||||
|
if (slot.topology < 0) {
|
||||||
|
selected = &slot;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (selected == nullptr || slot.last_used < selected->last_used) {
|
||||||
|
selected = &slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selected->session.reset();
|
||||||
|
selected->topology = topology;
|
||||||
|
selected->last_used = cuda_session_use_counter;
|
||||||
|
return &selected->session;
|
||||||
|
};
|
||||||
auto run_round = [&](int topology, bool depth, const char* phase, int position, int total) {
|
auto run_round = [&](int topology, bool depth, const char* phase, int position, int total) {
|
||||||
if (stop_requested.load(std::memory_order_relaxed)) {
|
if (stop_requested.load(std::memory_order_relaxed)) {
|
||||||
return;
|
return;
|
||||||
@@ -2756,16 +3300,43 @@ int global_search_all(const LocalRepairOptions& options) {
|
|||||||
GlobalTopologyState& state = states[topology];
|
GlobalTopologyState& state = states[topology];
|
||||||
const int round_seed = static_cast<int>(
|
const int round_seed = static_cast<int>(
|
||||||
1 + (static_cast<std::uint64_t>(static_cast<std::uint32_t>(options.seed)) +
|
1 + (static_cast<std::uint64_t>(static_cast<std::uint32_t>(options.seed)) +
|
||||||
static_cast<std::uint64_t>(completed_rounds + 1) * 15485863ULL +
|
static_cast<std::uint64_t>(state.visits + 1) * 15485863ULL +
|
||||||
static_cast<std::uint64_t>(topology + 1) * 32452843ULL) %
|
static_cast<std::uint64_t>(topology + 1) * 32452843ULL) %
|
||||||
2147483646ULL);
|
2147483646ULL);
|
||||||
std::cout << phase << " " << position << "/" << total
|
std::cout << phase << " " << position << "/" << total
|
||||||
<< " | topology " << topology
|
<< " | topology " << topology
|
||||||
<< " | " << (depth ? "depth" : "breadth") << std::endl;
|
<< " | " << (depth ? "depth" : "breadth") << std::endl;
|
||||||
|
cuda_search::BatchSession* cuda_session = acquire_cuda_session(topology);
|
||||||
const bool improved = run_global_topology_round(
|
const bool improved = run_global_topology_round(
|
||||||
options, state, depth, worker_count, stop_requested, round_seed);
|
options,
|
||||||
|
state,
|
||||||
|
depth,
|
||||||
|
worker_count,
|
||||||
|
stop_requested,
|
||||||
|
round_seed,
|
||||||
|
effective_cuda_chains,
|
||||||
|
cuda_session);
|
||||||
completed_rounds += 1;
|
completed_rounds += 1;
|
||||||
save_global_topology_state(options, state, improved, round_seed);
|
const auto checkpoint_now = std::chrono::steady_clock::now();
|
||||||
|
const bool checkpoint_due = state.last_checkpoint_at.time_since_epoch().count() == 0 ||
|
||||||
|
checkpoint_now - state.last_checkpoint_at >=
|
||||||
|
std::chrono::seconds(options.checkpoint_seconds);
|
||||||
|
if (checkpoint_due) {
|
||||||
|
if (commit_mergeable_checkpoint(
|
||||||
|
options,
|
||||||
|
run_identity,
|
||||||
|
state,
|
||||||
|
improved
|
||||||
|
? szilassi::checkpoint::CheckpointReason::Improvement
|
||||||
|
: szilassi::checkpoint::CheckpointReason::Periodic)) {
|
||||||
|
state.last_checkpoint_at = checkpoint_now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Legacy previews follow the durable checkpoint cadence. The current
|
||||||
|
// in-memory best is always committed on a clean stop below.
|
||||||
|
if (checkpoint_due) {
|
||||||
|
save_global_topology_state(options, state, improved, round_seed);
|
||||||
|
}
|
||||||
write_global_leaderboard(options, states);
|
write_global_leaderboard(options, states);
|
||||||
|
|
||||||
const double elapsed_minutes = std::chrono::duration<double>(
|
const double elapsed_minutes = std::chrono::duration<double>(
|
||||||
@@ -2801,9 +3372,9 @@ int global_search_all(const LocalRepairOptions& options) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<int> breadth_order(NUM_TOPOLOGIES);
|
std::vector<int> breadth_order = active_topologies;
|
||||||
std::iota(breadth_order.begin(), breadth_order.end(), 0);
|
const int active_count = static_cast<int>(breadth_order.size());
|
||||||
const int rotation = ((options.seed % NUM_TOPOLOGIES) + NUM_TOPOLOGIES) % NUM_TOPOLOGIES;
|
const int rotation = ((options.seed % active_count) + active_count) % active_count;
|
||||||
std::rotate(breadth_order.begin(), breadth_order.begin() + rotation, breadth_order.end());
|
std::rotate(breadth_order.begin(), breadth_order.begin() + rotation, breadth_order.end());
|
||||||
std::vector<int> missing;
|
std::vector<int> missing;
|
||||||
for (int topology : breadth_order) {
|
for (int topology : breadth_order) {
|
||||||
@@ -2817,8 +3388,7 @@ int global_search_all(const LocalRepairOptions& options) {
|
|||||||
|
|
||||||
int cycle = 0;
|
int cycle = 0;
|
||||||
while (!stop_requested.load(std::memory_order_relaxed)) {
|
while (!stop_requested.load(std::memory_order_relaxed)) {
|
||||||
std::vector<int> ranked(NUM_TOPOLOGIES);
|
std::vector<int> ranked = active_topologies;
|
||||||
std::iota(ranked.begin(), ranked.end(), 0);
|
|
||||||
std::sort(ranked.begin(), ranked.end(), [&](int a, int b) {
|
std::sort(ranked.begin(), ranked.end(), [&](int a, int b) {
|
||||||
if (states[a].has_state != states[b].has_state) {
|
if (states[a].has_state != states[b].has_state) {
|
||||||
return states[a].has_state;
|
return states[a].has_state;
|
||||||
@@ -2830,7 +3400,7 @@ int global_search_all(const LocalRepairOptions& options) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const bool exploration_cycle = cycle % 5 == 4;
|
const bool exploration_cycle = cycle % 5 == 4;
|
||||||
int keep = NUM_TOPOLOGIES;
|
int keep = active_count;
|
||||||
if (!exploration_cycle) {
|
if (!exploration_cycle) {
|
||||||
keep = cycle == 0 ? 30 : (cycle == 1 ? 16 : 8);
|
keep = cycle == 0 ? 30 : (cycle == 1 ? 16 : 8);
|
||||||
} else {
|
} else {
|
||||||
@@ -2852,14 +3422,25 @@ int global_search_all(const LocalRepairOptions& options) {
|
|||||||
|
|
||||||
watcher_done.store(true, std::memory_order_relaxed);
|
watcher_done.store(true, std::memory_order_relaxed);
|
||||||
watcher.join();
|
watcher.join();
|
||||||
|
for (int topology : active_topologies) {
|
||||||
|
GlobalTopologyState& state = states[topology];
|
||||||
|
if (state.run_visits == 0 || !load_global_topology_context(topology)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
commit_mergeable_checkpoint(
|
||||||
|
options,
|
||||||
|
run_identity,
|
||||||
|
state,
|
||||||
|
szilassi::checkpoint::CheckpointReason::Stop);
|
||||||
|
save_global_topology_state(options, state, true, options.seed);
|
||||||
|
}
|
||||||
if (!options.stop_file_path.empty()) {
|
if (!options.stop_file_path.empty()) {
|
||||||
std::error_code remove_error;
|
std::error_code remove_error;
|
||||||
std::filesystem::remove(options.stop_file_path, remove_error);
|
std::filesystem::remove(options.stop_file_path, remove_error);
|
||||||
}
|
}
|
||||||
write_global_leaderboard(options, states);
|
write_global_leaderboard(options, states);
|
||||||
|
|
||||||
std::vector<int> ranked(NUM_TOPOLOGIES);
|
std::vector<int> ranked = active_topologies;
|
||||||
std::iota(ranked.begin(), ranked.end(), 0);
|
|
||||||
std::sort(ranked.begin(), ranked.end(), [&](int a, int b) {
|
std::sort(ranked.begin(), ranked.end(), [&](int a, int b) {
|
||||||
if (states[a].has_state != states[b].has_state) {
|
if (states[a].has_state != states[b].has_state) {
|
||||||
return states[a].has_state;
|
return states[a].has_state;
|
||||||
@@ -3101,6 +3682,21 @@ int main(int argc, char* argv[]) {
|
|||||||
repair_options.stop_file_path = argv[++i];
|
repair_options.stop_file_path = argv[++i];
|
||||||
} else if (arg == "--global-dir" && i + 1 < argc) {
|
} else if (arg == "--global-dir" && i + 1 < argc) {
|
||||||
repair_options.global_dir = argv[++i];
|
repair_options.global_dir = argv[++i];
|
||||||
|
} else if (arg == "--topology-from" && i + 1 < argc) {
|
||||||
|
repair_options.topology_from = std::atoi(argv[++i]);
|
||||||
|
} else if (arg == "--topology-to" && i + 1 < argc) {
|
||||||
|
repair_options.topology_to = std::atoi(argv[++i]);
|
||||||
|
} else if (arg == "--cuda") {
|
||||||
|
repair_options.use_cuda = true;
|
||||||
|
} else if (arg == "--cuda-chains" && i + 1 < argc) {
|
||||||
|
repair_options.cuda_chains = std::max(0, std::atoi(argv[++i]));
|
||||||
|
} else if (arg == "--cuda-iters" && i + 1 < argc) {
|
||||||
|
repair_options.cuda_iterations = std::clamp(std::atoi(argv[++i]), 1, 64);
|
||||||
|
} else if (arg == "--checkpoint-seconds" && i + 1 < argc) {
|
||||||
|
repair_options.checkpoint_seconds = std::clamp(std::atoi(argv[++i]), 5, 3600);
|
||||||
|
} else if (arg == "--degeneracy-weight" && i + 1 < argc) {
|
||||||
|
repair_options.degeneracy_weight = std::clamp(
|
||||||
|
std::atof(argv[++i]), 0.0, 1.0);
|
||||||
} else if (arg == "--restarts" && i + 1 < argc) {
|
} else if (arg == "--restarts" && i + 1 < argc) {
|
||||||
repair_options.restarts = std::atoi(argv[++i]);
|
repair_options.restarts = std::atoi(argv[++i]);
|
||||||
} else if (arg == "--stagnation" && i + 1 < argc) {
|
} else if (arg == "--stagnation" && i + 1 < argc) {
|
||||||
@@ -3121,6 +3717,14 @@ int main(int argc, char* argv[]) {
|
|||||||
return study_shape(options);
|
return study_shape(options);
|
||||||
}
|
}
|
||||||
if (run_global_search) {
|
if (run_global_search) {
|
||||||
|
if (repair_options.topology_from < 0 ||
|
||||||
|
repair_options.topology_to >= NUM_TOPOLOGIES ||
|
||||||
|
repair_options.topology_from > repair_options.topology_to) {
|
||||||
|
std::cerr << "Invalid topology range: "
|
||||||
|
<< repair_options.topology_from << ".."
|
||||||
|
<< repair_options.topology_to << std::endl;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
return global_search_all(repair_options);
|
return global_search_all(repair_options);
|
||||||
}
|
}
|
||||||
if (run_batch_hunt) {
|
if (run_batch_hunt) {
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
format szilassi-global-search-v2
|
||||||
|
run_id d27a3577-93be-4570-ae15-6ac4dfb73ac8
|
||||||
|
node_id LOOKICH
|
||||||
|
seed 2005138652
|
||||||
|
topology_from 0
|
||||||
|
topology_to 58
|
||||||
|
backend cuda-fp32
|
||||||
|
cuda_math standard-fp32
|
||||||
|
cuda_chains_requested 0
|
||||||
|
cuda_chains_effective 61440
|
||||||
|
cuda_iterations_per_batch 64
|
||||||
|
cuda_depth_batches 6
|
||||||
|
cuda_session_cache 59
|
||||||
|
cpu_iterations_per_trial 50000
|
||||||
|
degeneracy_weight 0.02
|
||||||
|
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user