Compare commits
107 Commits
a1b43bee51
...
94014ef48a
Author | SHA1 | Date |
---|---|---|
|
94014ef48a | |
|
914ad4fc49 | |
|
36fb9b86cc | |
|
b20cc2513a | |
|
6700161699 | |
|
f6d8073be9 | |
|
21ceb2322b | |
|
ea5759cf1a | |
|
0251bf33f8 | |
|
19504dd938 | |
|
cb16be1bae | |
|
f573aaf92f | |
|
27ecb2b46d | |
|
dc49f0b33a | |
|
9ea6568cdb | |
|
b634ea7268 | |
|
487f771377 | |
|
b9c535f6c9 | |
|
dcefa93d29 | |
|
29d8efe18f | |
|
0d299affdb | |
|
f89628615a | |
|
81b35f20b9 | |
|
9bc234882a | |
|
49c80c8f53 | |
|
373fdec6d2 | |
|
fab86f8162 | |
|
62af2584d1 | |
|
d14ccfd1da | |
|
ece76e1a91 | |
|
d8c7a27214 | |
|
51eb1711b9 | |
|
2e09696a67 | |
|
0dc083e095 | |
|
0eea2189e3 | |
|
a742164bed | |
|
abb4cc26a4 | |
|
d14b7ee7ce | |
|
9cc4b9949b | |
|
08e44fb4bd | |
|
577f3d613c | |
|
88957ce805 | |
|
0dadfb73a7 | |
|
a58fe64c0e | |
|
82c535dbd1 | |
|
b79fc5a0f1 | |
|
f83ad3b2f4 | |
|
100a4bfd39 | |
|
fa07017299 | |
|
d457abc92f | |
|
1a070ab5f2 | |
|
4cfbdc08ec | |
|
b3153511ee | |
|
de853370be | |
|
1430e2f9a8 | |
|
ccc6abaa79 | |
|
0e5d8dfd25 | |
|
87d0a46552 | |
|
316f973fd8 | |
|
f7d28d7a65 | |
|
44571f5402 | |
|
9eff9ea8ef | |
|
8594c44bae | |
|
8917fc12f7 | |
|
6ab3c8c500 | |
|
f9aba7a7a6 | |
|
db0c7da8e7 | |
|
32a83e725c | |
|
1e9442bbd4 | |
|
fca412ea29 | |
|
99b784835b | |
|
0cf5450eb7 | |
|
053a34e30c | |
|
ca0f10b410 | |
|
a497a006ee | |
|
d19bc6b94b | |
|
82d36caecf | |
|
9fb3d78bca | |
|
5356be6649 | |
|
3e0fe06017 | |
|
e1083426fc | |
|
946ddcc572 | |
|
c6a2b5d324 | |
|
e4ce3739f0 | |
|
094fa76b58 | |
|
f3027671f5 | |
|
92760a217d | |
|
65f5fb3efa | |
|
b4b7e8f454 | |
|
15ccd0b615 | |
|
9cab2d3d4c | |
|
04fe36deb8 | |
|
a5238bc798 | |
|
2b1f1c92ab | |
|
8b0b0dbefa | |
|
699806aeff | |
|
0dfa4fea93 | |
|
43e5ac46b9 | |
|
85e78df690 | |
|
d69c45a82b | |
|
0da04237c7 | |
|
89ed1bd116 | |
|
ea9721b4cc | |
|
bd230f075f | |
|
914995fc82 | |
|
58681803e9 | |
|
76de033fe8 |
|
@ -1,4 +1,4 @@
|
||||||
cmake-build*/
|
\cmake-build*/
|
||||||
build/
|
build/
|
||||||
out/
|
out/
|
||||||
./cmake-build*/
|
./cmake-build*/
|
||||||
|
@ -8,4 +8,4 @@ massif.*
|
||||||
callgrind.*
|
callgrind.*
|
||||||
*.out.*
|
*.out.*
|
||||||
heaptrack.*
|
heaptrack.*
|
||||||
Rice_Cammeo_Osmancik.arff
|
vgcore.*
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Brett C++ Config 2" />
|
||||||
|
</state>
|
||||||
|
</component>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="BackendCodeEditorSettings">
|
||||||
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassCanBeFinal/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
|
||||||
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclarationHidesUncapturedLocal/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
|
||||||
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeprecatedOverridenMethod/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantFwdClassOrEnumSpecifier/@EntryIndexedValue" value="SUGGESTION" type="string" />
|
||||||
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantQualifierADL/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
|
||||||
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTooWideScopeInitStatement/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
|
||||||
|
<option name="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020fields/@EntryIndexedValue" value="<NamingElement Priority="11"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="class field" /><type Name="struct field" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="m_" Suffix="" Style="aa_bb" /></NamingElement>" type="string" />
|
||||||
|
<option name="/Default/CodeStyle/Naming/CppNaming/Rules/=Enums/@EntryIndexedValue" value="<NamingElement Priority="3"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="enum" /></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AA_BB" /></NamingElement>" type="string" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="CppClassCanBeFinal" enabled="false" level="HINT" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="CppDeclarationHidesUncapturedLocal" enabled="false" level="HINT" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="CppTooWideScopeInitStatement" enabled="false" level="HINT" enabled_by_default="false" />
|
||||||
|
</profile>
|
||||||
|
</component>
|
|
@ -1,4 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
|
<component name="CMakePythonSetting">
|
||||||
|
<option name="pythonIntegrationState" value="YES" />
|
||||||
|
</component>
|
||||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
</project>
|
</project>
|
113
CMakeLists.txt
113
CMakeLists.txt
|
@ -1,5 +1,33 @@
|
||||||
cmake_minimum_required(VERSION 3.25)
|
cmake_minimum_required(VERSION 3.25)
|
||||||
project(blt-gp VERSION 0.1.50)
|
|
||||||
|
macro(sanitizers target_name)
|
||||||
|
if (${ENABLE_ADDRSAN} MATCHES ON)
|
||||||
|
target_compile_options(${target_name} PRIVATE -fsanitize=address)
|
||||||
|
target_link_options(${target_name} PRIVATE -fsanitize=address)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (${ENABLE_UBSAN} MATCHES ON)
|
||||||
|
target_compile_options(${target_name} PRIVATE -fsanitize=undefined)
|
||||||
|
target_link_options(${target_name} PRIVATE -fsanitize=undefined)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (${ENABLE_TSAN} MATCHES ON)
|
||||||
|
target_compile_options(${target_name} PRIVATE -fsanitize=thread)
|
||||||
|
target_link_options(${target_name} PRIVATE -fsanitize=thread)
|
||||||
|
endif ()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro(compile_options target_name)
|
||||||
|
if (NOT ${MOLD} STREQUAL MOLD-NOTFOUND)
|
||||||
|
target_compile_options(${target_name} PUBLIC -fuse-ld=mold)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
||||||
|
target_link_options(${target_name} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
||||||
|
sanitizers(${target_name})
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
project(blt-gp VERSION 0.5.27)
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
|
||||||
|
@ -9,7 +37,8 @@ option(ENABLE_TSAN "Enable the thread data race sanitizer" OFF)
|
||||||
option(BUILD_EXAMPLES "Build example programs. This will build with CTest" OFF)
|
option(BUILD_EXAMPLES "Build example programs. This will build with CTest" OFF)
|
||||||
option(BUILD_GP_TESTS "Build test programs." OFF)
|
option(BUILD_GP_TESTS "Build test programs." OFF)
|
||||||
option(DEBUG_LEVEL "Enable debug features which prints extra information to the console, might slow processing down. [0, 3)" 0)
|
option(DEBUG_LEVEL "Enable debug features which prints extra information to the console, might slow processing down. [0, 3)" 0)
|
||||||
option(TRACK_ALLOCATIONS "Track total allocations. Can be accessed with blt::gp::tracker" OFF)
|
option(BLT_GP_DEBUG_CHECK_TREES "Enable checking of trees after every operation" OFF)
|
||||||
|
option(BLT_GP_DEBUG_TRACK_ALLOCATIONS "Track total allocations. Can be accessed with blt::gp::tracker" OFF)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
@ -18,6 +47,10 @@ find_package(Threads REQUIRED)
|
||||||
|
|
||||||
SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -g")
|
SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -g")
|
||||||
|
|
||||||
|
if (NOT ${CMAKE_BUILD_TYPE})
|
||||||
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (NOT TARGET BLT)
|
if (NOT TARGET BLT)
|
||||||
add_subdirectory(lib/blt)
|
add_subdirectory(lib/blt)
|
||||||
endif ()
|
endif ()
|
||||||
|
@ -27,8 +60,7 @@ file(GLOB_RECURSE PROJECT_BUILD_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
|
||||||
|
|
||||||
add_library(blt-gp ${PROJECT_BUILD_FILES})
|
add_library(blt-gp ${PROJECT_BUILD_FILES})
|
||||||
|
|
||||||
target_compile_options(blt-gp PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
compile_options(blt-gp)
|
||||||
target_link_options(blt-gp PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
|
||||||
|
|
||||||
find_program(MOLD "mold")
|
find_program(MOLD "mold")
|
||||||
|
|
||||||
|
@ -45,85 +77,52 @@ if (${TRACK_ALLOCATIONS})
|
||||||
target_compile_definitions(blt-gp PRIVATE BLT_TRACK_ALLOCATIONS=1)
|
target_compile_definitions(blt-gp PRIVATE BLT_TRACK_ALLOCATIONS=1)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (${ENABLE_ADDRSAN} MATCHES ON)
|
|
||||||
target_compile_options(blt-gp PRIVATE -fsanitize=address)
|
|
||||||
target_link_options(blt-gp PRIVATE -fsanitize=address)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (${ENABLE_UBSAN} MATCHES ON)
|
|
||||||
target_compile_options(blt-gp PRIVATE -fsanitize=undefined)
|
|
||||||
target_link_options(blt-gp PRIVATE -fsanitize=undefined)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (${ENABLE_TSAN} MATCHES ON)
|
|
||||||
target_compile_options(blt-gp PRIVATE -fsanitize=thread)
|
|
||||||
target_link_options(blt-gp PRIVATE -fsanitize=thread)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
macro(blt_add_project name source type)
|
macro(blt_add_project name source type)
|
||||||
|
|
||||||
project(${name}-${type})
|
project(${name}-${type})
|
||||||
|
|
||||||
#add_compile_options(-fuse-ld=mold)
|
|
||||||
|
|
||||||
add_executable(${name}-${type} ${source})
|
add_executable(${name}-${type} ${source})
|
||||||
|
|
||||||
target_link_libraries(${name}-${type} PRIVATE BLT blt-gp Threads::Threads)
|
target_link_libraries(${name}-${type} PRIVATE BLT blt-gp Threads::Threads)
|
||||||
|
|
||||||
target_compile_options(${name}-${type} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
compile_options(${name}-${type})
|
||||||
target_link_options(${name}-${type} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
|
|
||||||
target_compile_definitions(${name}-${type} PRIVATE BLT_DEBUG_LEVEL=${DEBUG_LEVEL})
|
target_compile_definitions(${name}-${type} PRIVATE BLT_DEBUG_LEVEL=${DEBUG_LEVEL})
|
||||||
|
|
||||||
if (${TRACK_ALLOCATIONS})
|
if (${TRACK_ALLOCATIONS})
|
||||||
target_compile_definitions(${name}-${type} PRIVATE BLT_TRACK_ALLOCATIONS=1)
|
target_compile_definitions(${name}-${type} PRIVATE BLT_TRACK_ALLOCATIONS=1)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (${ENABLE_ADDRSAN} MATCHES ON)
|
add_test(NAME ${name}-${type} COMMAND ${name}-${type})
|
||||||
target_compile_options(${name}-${type} PRIVATE -fsanitize=address)
|
|
||||||
target_link_options(${name}-${type} PRIVATE -fsanitize=address)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (${ENABLE_UBSAN} MATCHES ON)
|
set_property(TEST ${name}-${type} PROPERTY FAIL_REGULAR_EXPRESSION "FAIL;ERROR;FATAL;exception")
|
||||||
target_compile_options(${name}-${type} PRIVATE -fsanitize=undefined)
|
|
||||||
target_link_options(${name}-${type} PRIVATE -fsanitize=undefined)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (${ENABLE_TSAN} MATCHES ON)
|
|
||||||
target_compile_options(${name}-${type} PRIVATE -fsanitize=thread)
|
|
||||||
target_link_options(${name}-${type} PRIVATE -fsanitize=thread)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
add_test(NAME ${name} COMMAND ${name}-${type})
|
|
||||||
|
|
||||||
# set (passRegex "Pass" "Passed" "PASS" "PASSED")
|
|
||||||
# set (failRegex "WARN" "FAIL" "ERROR" "FATAL")
|
|
||||||
set(failRegex "\\[WARN\\]" "FAIL" "ERROR" "FATAL" "exception")
|
|
||||||
|
|
||||||
# set_property (TEST ${name} PROPERTY PASS_REGULAR_EXPRESSION "${passRegex}")
|
|
||||||
set_property(TEST ${name} PROPERTY FAIL_REGULAR_EXPRESSION "${failRegex}")
|
|
||||||
|
|
||||||
project(blt-gp)
|
project(blt-gp)
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
if (${BUILD_EXAMPLES})
|
if (${BUILD_EXAMPLES})
|
||||||
|
|
||||||
blt_add_project(blt-symbolic-regression examples/symbolic_regression.cpp example)
|
blt_add_project(blt-symbolic-regression examples/src/symbolic_regression.cpp example)
|
||||||
blt_add_project(blt-rice-classification examples/rice_classification.cpp example)
|
blt_add_project(blt-rice-classification examples/src/rice_classification.cpp example)
|
||||||
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (${BUILD_GP_TESTS})
|
if (${BUILD_GP_TESTS})
|
||||||
|
|
||||||
blt_add_project(blt-stack tests/stack_tests.cpp test)
|
# blt_add_project(blt-stack tests/old/stack_tests.cpp test)
|
||||||
blt_add_project(blt-eval tests/evaluation_tests.cpp test)
|
# blt_add_project(blt-eval tests/old/evaluation_tests.cpp test)
|
||||||
blt_add_project(blt-order tests/order_tests.cpp test)
|
# blt_add_project(blt-order tests/old/order_tests.cpp test)
|
||||||
#blt_add_project(blt-destructor tests/destructor_test.cpp test)
|
#blt_add_project(blt-destructor tests/destructor_test.cpp test)
|
||||||
blt_add_project(blt-gp1 tests/gp_test_1.cpp test)
|
# blt_add_project(blt-gp1 tests/old/gp_test_1.cpp test)
|
||||||
blt_add_project(blt-gp2 tests/gp_test_2.cpp test)
|
# blt_add_project(blt-gp2 tests/old/gp_test_2.cpp test)
|
||||||
blt_add_project(blt-gp3 tests/gp_test_3.cpp test)
|
# blt_add_project(blt-gp3 tests/old/gp_test_3.cpp test)
|
||||||
blt_add_project(blt-gp4 tests/gp_test_4.cpp test)
|
# blt_add_project(blt-gp4 tests/old/gp_test_4.cpp test)
|
||||||
blt_add_project(blt-gp5 tests/gp_test_5.cpp test)
|
# blt_add_project(blt-gp5 tests/old/gp_test_5.cpp test)
|
||||||
blt_add_project(blt-gp6 tests/gp_test_6.cpp test)
|
# blt_add_project(blt-gp6 tests/old/gp_test_6.cpp test)
|
||||||
blt_add_project(blt-gp7 tests/gp_test_7.cpp test)
|
# blt_add_project(blt-gp7 tests/old/gp_test_7.cpp test)
|
||||||
|
|
||||||
|
blt_add_project(blt-symbolic-regression tests/symbolic_regression_test.cpp test)
|
||||||
|
blt_add_project(blt-drop tests/drop_test.cpp test)
|
||||||
|
blt_add_project(blt-drop-2-type tests/2_type_drop_test.cpp test)
|
||||||
|
blt_add_project(blt-serialization tests/serialization_test.cpp test)
|
||||||
|
|
||||||
endif ()
|
endif ()
|
|
@ -0,0 +1,230 @@
|
||||||
|
bool type_aware_crossover_t::apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2)
|
||||||
|
{
|
||||||
|
if (p1.size() < config.min_tree_size || p2.size() < config.min_tree_size)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
tree_t::subtree_point_t point1, point2;
|
||||||
|
if (config.traverse)
|
||||||
|
{
|
||||||
|
point1 = p1.select_subtree_traverse(config.terminal_chance, config.depth_multiplier);
|
||||||
|
if (const auto val = p2.select_subtree_traverse(point1.type, config.max_crossover_tries, config.terminal_chance, config.depth_multiplier))
|
||||||
|
point2 = *val;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
point1 = p1.select_subtree(config.terminal_chance);
|
||||||
|
if (const auto val = p2.select_subtree(point1.type, config.max_crossover_tries, config.terminal_chance))
|
||||||
|
point2 = *val;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& p1_operator = p1.get_operator(point1.pos);
|
||||||
|
const auto& p2_operator = p2.get_operator(point2.pos);
|
||||||
|
|
||||||
|
// If either is a terminal (value), just do normal subtree crossover
|
||||||
|
if (p1_operator.is_value() || p2_operator.is_value())
|
||||||
|
{
|
||||||
|
c1.swap_subtrees(point1, c2, point2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& p1_info = program.get_operator_info(p1_operator.id());
|
||||||
|
const auto& p2_info = program.get_operator_info(p2_operator.id());
|
||||||
|
|
||||||
|
// Find the child subtrees of both operators
|
||||||
|
thread_local tracked_vector<tree_t::child_t> children_data_p1;
|
||||||
|
thread_local tracked_vector<tree_t::child_t> children_data_p2;
|
||||||
|
children_data_p1.clear();
|
||||||
|
children_data_p2.clear();
|
||||||
|
|
||||||
|
p1.find_child_extends(children_data_p1, point1.pos, p1_info.argument_types.size());
|
||||||
|
p2.find_child_extends(children_data_p2, point2.pos, p2_info.argument_types.size());
|
||||||
|
|
||||||
|
// Check if all types are identical but possibly in different order
|
||||||
|
bool same_types_different_order = p1_info.argument_types.size() == p2_info.argument_types.size();
|
||||||
|
|
||||||
|
if (same_types_different_order)
|
||||||
|
{
|
||||||
|
// Create frequency counts of types in both operators
|
||||||
|
std::unordered_map<type_id, size_t> type_counts_p1;
|
||||||
|
std::unordered_map<type_id, size_t> type_counts_p2;
|
||||||
|
|
||||||
|
for (const auto& type : p1_info.argument_types)
|
||||||
|
type_counts_p1[type.id]++;
|
||||||
|
|
||||||
|
for (const auto& type : p2_info.argument_types)
|
||||||
|
type_counts_p2[type.id]++;
|
||||||
|
|
||||||
|
// Check if the type counts match
|
||||||
|
for (const auto& [type, count] : type_counts_p1)
|
||||||
|
{
|
||||||
|
if (type_counts_p2[type] != count)
|
||||||
|
{
|
||||||
|
same_types_different_order = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (same_types_different_order)
|
||||||
|
{
|
||||||
|
// Create a mapping from p1's argument positions to p2's positions
|
||||||
|
std::vector<size_t> arg_mapping(p1_info.argument_types.size(), (size_t)-1);
|
||||||
|
std::vector<bool> p2_used(p2_info.argument_types.size(), false);
|
||||||
|
|
||||||
|
// First pass: match exact types in order
|
||||||
|
for (size_t i = 0; i < p1_info.argument_types.size(); i++)
|
||||||
|
{
|
||||||
|
for (size_t j = 0; j < p2_info.argument_types.size(); j++)
|
||||||
|
{
|
||||||
|
if (!p2_used[j] && p1_info.argument_types[i].id == p2_info.argument_types[j].id)
|
||||||
|
{
|
||||||
|
arg_mapping[i] = j;
|
||||||
|
p2_used[j] = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy operators first
|
||||||
|
auto& c1_temp = tree_t::get_thread_local(program);
|
||||||
|
auto& c2_temp = tree_t::get_thread_local(program);
|
||||||
|
c1_temp.clear(program);
|
||||||
|
c2_temp.clear(program);
|
||||||
|
|
||||||
|
// Create new operators with the same return types
|
||||||
|
c1_temp.insert_operator({
|
||||||
|
program.get_typesystem().get_type(p2_info.return_type).size(),
|
||||||
|
p2_operator.id(),
|
||||||
|
program.is_operator_ephemeral(p2_operator.id()),
|
||||||
|
program.get_operator_flags(p2_operator.id())
|
||||||
|
});
|
||||||
|
|
||||||
|
c2_temp.insert_operator({
|
||||||
|
program.get_typesystem().get_type(p1_info.return_type).size(),
|
||||||
|
p1_operator.id(),
|
||||||
|
program.is_operator_ephemeral(p1_operator.id()),
|
||||||
|
program.get_operator_flags(p1_operator.id())
|
||||||
|
});
|
||||||
|
|
||||||
|
// Copy child subtrees according to the mapping
|
||||||
|
for (size_t i = 0; i < p1_info.argument_types.size(); i++)
|
||||||
|
{
|
||||||
|
auto& p1_child = children_data_p1[i];
|
||||||
|
auto& p2_child = children_data_p2[arg_mapping[i]];
|
||||||
|
|
||||||
|
tree_t p1_subtree(program);
|
||||||
|
tree_t p2_subtree(program);
|
||||||
|
|
||||||
|
p1.copy_subtree(tree_t::subtree_point_t(p1_child.start), p1_child.end, p1_subtree);
|
||||||
|
p2.copy_subtree(tree_t::subtree_point_t(p2_child.start), p2_child.end, p2_subtree);
|
||||||
|
|
||||||
|
c1_temp.insert_subtree(tree_t::subtree_point_t(c1_temp.size()), p2_subtree);
|
||||||
|
c2_temp.insert_subtree(tree_t::subtree_point_t(c2_temp.size()), p1_subtree);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the original subtrees with our new reordered ones
|
||||||
|
c1.replace_subtree(point1, c1_temp);
|
||||||
|
c2.replace_subtree(point2, c2_temp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If types don't match exactly, fall back to simple operator swap
|
||||||
|
// but we need to ensure the children are compatible
|
||||||
|
|
||||||
|
// Create new operators with swapped operators but appropriate children
|
||||||
|
auto& c1_temp = tree_t::get_thread_local(program);
|
||||||
|
auto& c2_temp = tree_t::get_thread_local(program);
|
||||||
|
c1_temp.clear(program);
|
||||||
|
c2_temp.clear(program);
|
||||||
|
|
||||||
|
c1_temp.insert_operator({
|
||||||
|
program.get_typesystem().get_type(p2_info.return_type).size(),
|
||||||
|
p2_operator.id(),
|
||||||
|
program.is_operator_ephemeral(p2_operator.id()),
|
||||||
|
program.get_operator_flags(p2_operator.id())
|
||||||
|
});
|
||||||
|
|
||||||
|
c2_temp.insert_operator({
|
||||||
|
program.get_typesystem().get_type(p1_info.return_type).size(),
|
||||||
|
p1_operator.id(),
|
||||||
|
program.is_operator_ephemeral(p1_operator.id()),
|
||||||
|
program.get_operator_flags(p1_operator.id())
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a mapping of which children we can reuse and which need to be regenerated
|
||||||
|
for (size_t i = 0; i < p2_info.argument_types.size(); i++)
|
||||||
|
{
|
||||||
|
const auto& needed_type = p2_info.argument_types[i];
|
||||||
|
bool found_match = false;
|
||||||
|
|
||||||
|
// Try to find a matching child from p1
|
||||||
|
for (size_t j = 0; j < p1_info.argument_types.size(); j++)
|
||||||
|
{
|
||||||
|
if (needed_type.id == p1_info.argument_types[j].id)
|
||||||
|
{
|
||||||
|
// Copy this child subtree from p1
|
||||||
|
auto& p1_child = children_data_p1[j];
|
||||||
|
tree_t p1_subtree(program);
|
||||||
|
p1.copy_subtree(tree_t::subtree_point_t(p1_child.start), p1_child.end, p1_subtree);
|
||||||
|
c1_temp.insert_subtree(tree_t::subtree_point_t(c1_temp.size()), p1_subtree);
|
||||||
|
found_match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_match)
|
||||||
|
{
|
||||||
|
// If no matching child, we need to generate a new subtree of the correct type
|
||||||
|
auto& tree = tree_t::get_thread_local(program);
|
||||||
|
tree.clear(program);
|
||||||
|
config.generator.get().generate(tree, {program, needed_type.id, config.replacement_min_depth, config.replacement_max_depth});
|
||||||
|
c1_temp.insert_subtree(tree_t::subtree_point_t(c1_temp.size()), tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the same for the other direction (c2)
|
||||||
|
for (size_t i = 0; i < p1_info.argument_types.size(); i++)
|
||||||
|
{
|
||||||
|
const auto& needed_type = p1_info.argument_types[i];
|
||||||
|
bool found_match = false;
|
||||||
|
|
||||||
|
// Try to find a matching child from p2
|
||||||
|
for (size_t j = 0; j < p2_info.argument_types.size(); j++)
|
||||||
|
{
|
||||||
|
if (needed_type.id == p2_info.argument_types[j].id)
|
||||||
|
{
|
||||||
|
// Copy this child subtree from p2
|
||||||
|
auto& p2_child = children_data_p2[j];
|
||||||
|
tree_t p2_subtree(program);
|
||||||
|
p2.copy_subtree(tree_t::subtree_point_t(p2_child.start), p2_child.end, p2_subtree);
|
||||||
|
c2_temp.insert_subtree(tree_t::subtree_point_t(c2_temp.size()), p2_subtree);
|
||||||
|
found_match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_match)
|
||||||
|
{
|
||||||
|
// If no matching child, we need to generate a new subtree of the correct type
|
||||||
|
auto& tree = tree_t::get_thread_local(program);
|
||||||
|
tree.clear(program);
|
||||||
|
config.generator.get().generate(tree, {program, needed_type.id, config.replacement_min_depth, config.replacement_max_depth});
|
||||||
|
c2_temp.insert_subtree(tree_t::subtree_point_t(c2_temp.size()), tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the original subtrees with our new ones
|
||||||
|
c1.replace_subtree(point1, c1_temp);
|
||||||
|
c2.replace_subtree(point2, c2_temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if BLT_DEBUG_LEVEL >= 2
|
||||||
|
if (!c1.check(detail::debug::context_ptr) || !c2.check(detail::debug::context_ptr))
|
||||||
|
throw std::runtime_error("Tree check failed");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,674 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
@ -0,0 +1,48 @@
|
||||||
|
# blt-gp
|
||||||
|
Genetic Programming (GP) library for C++. Integrates directly into the C++ type system, a safe replacement for lilgp without performance compromises.
|
||||||
|
|
||||||
|
## Easy to use and safe, without compromise
|
||||||
|
Using blt-gp is very easy, import the program header, define your operators, and then call the relevant functions to set up the program to your liking.
|
||||||
|
Concrete examples can be found in the example folder and compiled using the CMake argument `-DBUILD_EXAMPLES=ON`
|
||||||
|
|
||||||
|
Operator example:
|
||||||
|
```c++
|
||||||
|
// Constructor takes an optional string that describes the function of the operator. Used in printing.
|
||||||
|
blt::gp::operation_t add([](float a, float b) {
|
||||||
|
return a + b;
|
||||||
|
}, "add");
|
||||||
|
```
|
||||||
|
Operators can return different types or have differing types as arguments, this will automatically be recognized by the library.
|
||||||
|
```c++
|
||||||
|
// Any callable type can be passed as the function parameter
|
||||||
|
// so long as the function exists for the lifetime of the gp_program object.
|
||||||
|
bool compare_impl(bool type, float a, float b)
|
||||||
|
{
|
||||||
|
return type ? (a > b) : (a < b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
blt::gp::operation_t compare(compare_impl, "compare");
|
||||||
|
```
|
||||||
|
Please note that if a type doesn't have a way to produce a terminal, or doesn't have a way of converting from a type that has a terminal, you will get an error.
|
||||||
|
|
||||||
|
Defining your fitness function is just as easy:
|
||||||
|
```c++
|
||||||
|
const auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fitness_t& fitness, blt::size_t current_index) {
|
||||||
|
// Evaluate your fitness
|
||||||
|
// ...
|
||||||
|
// Write to the fitness out parameter. Only "adjusted_fitness" is used during evaluation.
|
||||||
|
fitness.raw_fitness = static_cast<double>(fitness.hits);
|
||||||
|
fitness.standardized_fitness = fitness.raw_fitness;
|
||||||
|
// Higher values = better. Should be bounded [0, 1]
|
||||||
|
fitness.adjusted_fitness = 1.0 - (1.0 / (1.0 + fitness.standardized_fitness));
|
||||||
|
// returning true from this function ends the evaluation of the program, as this signals that a valid solution was found.
|
||||||
|
// This function is allowed to return void.
|
||||||
|
return static_cast<blt::size_t>(fitness.hits) == training_cases.size();
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
## Bibliography
|
||||||
|
Rice (Cammeo and Osmancik) [Dataset]. (2019). UCI Machine Learning Repository. https://doi.org/10.24432/C5MW4Z.
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/python3
|
#!python3
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,44 @@
|
||||||
|
{ pkgs ? (import <nixpkgs> {
|
||||||
|
config.allowUnfree = true;
|
||||||
|
config.segger-jlink.acceptLicense = true;
|
||||||
|
}), ... }:
|
||||||
|
pkgs.mkShell
|
||||||
|
{
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
cmake
|
||||||
|
gcc
|
||||||
|
clang
|
||||||
|
emscripten
|
||||||
|
ninja
|
||||||
|
renderdoc
|
||||||
|
valgrind
|
||||||
|
gtest
|
||||||
|
opentelemetry-cpp
|
||||||
|
opentelemetry-cpp.dev
|
||||||
|
];
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
pkg-config
|
||||||
|
opentelemetry-cpp
|
||||||
|
opentelemetry-cpp.dev
|
||||||
|
];
|
||||||
|
propagatedBuildInputs = with pkgs; [
|
||||||
|
abseil-cpp
|
||||||
|
protobuf
|
||||||
|
grpc
|
||||||
|
prometheus-cpp
|
||||||
|
prometheus-cpp.dev
|
||||||
|
openssl
|
||||||
|
openssl.dev
|
||||||
|
opentelemetry-cpp
|
||||||
|
opentelemetry-cpp.dev
|
||||||
|
civetweb
|
||||||
|
civetweb.dev
|
||||||
|
c-ares
|
||||||
|
c-ares.dev
|
||||||
|
nlohmann_json
|
||||||
|
glibc
|
||||||
|
glibc.dev
|
||||||
|
curl
|
||||||
|
];
|
||||||
|
LD_LIBRARY_PATH="/run/opengl-driver/lib:/run/opengl-driver-32/lib";
|
||||||
|
}
|
|
@ -1,41 +0,0 @@
|
||||||
Performance counter stats for './cmake-build-release/blt-symbolic-regression-example' (30 runs):
|
|
||||||
|
|
||||||
24,277,728,279 branches ( +- 19.01% ) (20.47%)
|
|
||||||
76,457,616 branch-misses # 0.31% of all branches ( +- 17.97% ) (21.41%)
|
|
||||||
14,213,192 cache-misses # 4.73% of all cache refs ( +- 14.24% ) (22.52%)
|
|
||||||
300,581,049 cache-references ( +- 21.08% ) (23.68%)
|
|
||||||
48,914,779,668 cycles ( +- 19.65% ) (24.80%)
|
|
||||||
123,068,193,359 instructions # 2.52 insn per cycle ( +- 19.44% ) (25.09%)
|
|
||||||
0 alignment-faults
|
|
||||||
4,202 cgroup-switches ( +- 13.56% )
|
|
||||||
115,962 faults ( +- 10.95% )
|
|
||||||
871,101,993 ns duration_time ( +- 13.40% )
|
|
||||||
11,507,605,674 ns user_time ( +- 3.56% )
|
|
||||||
299,016,204 ns system_time ( +- 3.32% )
|
|
||||||
41,446,831,795 L1-dcache-loads ( +- 19.28% ) (24.69%)
|
|
||||||
167,603,194 L1-dcache-load-misses # 0.40% of all L1-dcache accesses ( +- 22.47% ) (23.95%)
|
|
||||||
81,992,073 L1-dcache-prefetches ( +- 25.34% ) (23.24%)
|
|
||||||
350,398,072 L1-icache-loads ( +- 15.30% ) (22.70%)
|
|
||||||
909,504 L1-icache-load-misses # 0.26% of all L1-icache accesses ( +- 14.46% ) (22.18%)
|
|
||||||
14,271,381 dTLB-loads ( +- 20.04% ) (21.90%)
|
|
||||||
1,559,972 dTLB-load-misses # 10.93% of all dTLB cache accesses ( +- 14.74% ) (21.39%)
|
|
||||||
246,888 iTLB-loads ( +- 21.69% ) (20.54%)
|
|
||||||
403,152 iTLB-load-misses # 163.29% of all iTLB cache accesses ( +- 13.35% ) (19.94%)
|
|
||||||
210,585,840 l2_request_g1.all_no_prefetch ( +- 20.07% ) (19.93%)
|
|
||||||
115,962 page-faults ( +- 10.95% )
|
|
||||||
115,958 page-faults:u ( +- 10.95% )
|
|
||||||
3 page-faults:k ( +- 4.54% )
|
|
||||||
41,209,739,257 L1-dcache-loads ( +- 19.02% ) (19.60%)
|
|
||||||
181,755,898 L1-dcache-load-misses # 0.44% of all L1-dcache accesses ( +- 20.60% ) (20.01%)
|
|
||||||
<not supported> LLC-loads
|
|
||||||
<not supported> LLC-load-misses
|
|
||||||
425,056,352 L1-icache-loads ( +- 12.27% ) (20.43%)
|
|
||||||
1,076,486 L1-icache-load-misses # 0.31% of all L1-icache accesses ( +- 10.84% ) (20.98%)
|
|
||||||
15,418,419 dTLB-loads ( +- 17.74% ) (21.24%)
|
|
||||||
1,648,473 dTLB-load-misses # 11.55% of all dTLB cache accesses ( +- 13.11% ) (20.94%)
|
|
||||||
325,141 iTLB-loads ( +- 26.87% ) (20.80%)
|
|
||||||
459,828 iTLB-load-misses # 186.25% of all iTLB cache accesses ( +- 11.50% ) (20.34%)
|
|
||||||
94,270,593 L1-dcache-prefetches ( +- 22.82% ) (20.09%)
|
|
||||||
<not supported> L1-dcache-prefetch-misses
|
|
||||||
|
|
||||||
0.871 +- 0.117 seconds time elapsed ( +- 13.40% )
|
|
1374
dhat.out.293761
1374
dhat.out.293761
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,64 @@
|
||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLT_GP_EXAMPLESEXAMPLES_BASE_H
|
||||||
|
#define BLT_GP_EXAMPLESEXAMPLES_BASE_H
|
||||||
|
|
||||||
|
#include <blt/gp/program.h>
|
||||||
|
|
||||||
|
namespace blt::gp::example
|
||||||
|
{
|
||||||
|
class example_base_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<typename SEED>
|
||||||
|
example_base_t(SEED&& seed, const prog_config_t& config): program{std::forward<SEED>(seed), config}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
example_base_t& set_crossover_selection(selection_t& sel)
|
||||||
|
{
|
||||||
|
crossover_sel = &sel;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
example_base_t& set_mutation_selection(selection_t& sel)
|
||||||
|
{
|
||||||
|
mutation_sel = &sel;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
example_base_t& set_reproduction_selection(selection_t& sel)
|
||||||
|
{
|
||||||
|
reproduction_sel = &sel;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
gp_program& get_program() { return program; }
|
||||||
|
const gp_program& get_program() const { return program; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
gp_program program;
|
||||||
|
selection_t* crossover_sel = nullptr;
|
||||||
|
selection_t* mutation_sel = nullptr;
|
||||||
|
selection_t* reproduction_sel = nullptr;
|
||||||
|
std::function<bool(const tree_t&, fitness_t&, size_t)> fitness_function_ref;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //BLT_GP_EXAMPLESEXAMPLES_BASE_H
|
|
@ -1,33 +0,0 @@
|
||||||
#pragma once
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 Brett Terpstra
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BLT_GP_OPERATIONS_COMMON_H
|
|
||||||
#define BLT_GP_OPERATIONS_COMMON_H
|
|
||||||
|
|
||||||
#include <blt/gp/program.h>
|
|
||||||
|
|
||||||
blt::gp::operation_t add([](float a, float b) { return a + b; }, "add");
|
|
||||||
blt::gp::operation_t sub([](float a, float b) { return a - b; }, "sub");
|
|
||||||
blt::gp::operation_t mul([](float a, float b) { return a * b; }, "mul");
|
|
||||||
blt::gp::operation_t pro_div([](float a, float b) { return b == 0.0f ? 1.0f : a / b; }, "div");
|
|
||||||
blt::gp::operation_t op_sin([](float a) { return std::sin(a); }, "sin");
|
|
||||||
blt::gp::operation_t op_cos([](float a) { return std::cos(a); }, "cos");
|
|
||||||
blt::gp::operation_t op_exp([](float a) { return std::exp(a); }, "exp");
|
|
||||||
blt::gp::operation_t op_log([](float a) { return a == 0.0f ? 0.0f : std::log(a); }, "log");
|
|
||||||
|
|
||||||
#endif //BLT_GP_OPERATIONS_COMMON_H
|
|
|
@ -1,353 +0,0 @@
|
||||||
/*
|
|
||||||
* This rice classification example uses data from the UC Irvine Machine Learning repository.
|
|
||||||
* The data for this example can be found at:
|
|
||||||
* https://archive.ics.uci.edu/dataset/545/rice+cammeo+and+osmancik
|
|
||||||
*
|
|
||||||
* Copyright (C) 2024 Brett Terpstra
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#include <blt/gp/program.h>
|
|
||||||
#include <blt/profiling/profiler_v2.h>
|
|
||||||
#include <blt/gp/tree.h>
|
|
||||||
#include <blt/std/logging.h>
|
|
||||||
#include <blt/std/format.h>
|
|
||||||
#include <blt/parse/argparse.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include "operations_common.h"
|
|
||||||
#include "blt/fs/loader.h"
|
|
||||||
|
|
||||||
static const auto SEED_FUNC = [] { return std::random_device()(); };
|
|
||||||
|
|
||||||
enum class rice_type_t
|
|
||||||
{
|
|
||||||
Cammeo,
|
|
||||||
Osmancik
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rice_record
|
|
||||||
{
|
|
||||||
float area;
|
|
||||||
float perimeter;
|
|
||||||
float major_axis_length;
|
|
||||||
float minor_axis_length;
|
|
||||||
float eccentricity;
|
|
||||||
float convex_area;
|
|
||||||
float extent;
|
|
||||||
rice_type_t type;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<rice_record> training_cases;
|
|
||||||
std::vector<rice_record> testing_cases;
|
|
||||||
|
|
||||||
blt::gp::prog_config_t config = blt::gp::prog_config_t()
|
|
||||||
.set_initial_min_tree_size(2)
|
|
||||||
.set_initial_max_tree_size(6)
|
|
||||||
.set_elite_count(2)
|
|
||||||
.set_crossover_chance(0.9)
|
|
||||||
.set_mutation_chance(0.1)
|
|
||||||
.set_reproduction_chance(0)
|
|
||||||
.set_max_generations(50)
|
|
||||||
.set_pop_size(5000)
|
|
||||||
.set_thread_count(0);
|
|
||||||
|
|
||||||
blt::gp::gp_program program{SEED_FUNC, config};
|
|
||||||
|
|
||||||
auto lit = blt::gp::operation_t([]() {
|
|
||||||
return program.get_random().get_float(-32000.0f, 32000.0f);
|
|
||||||
}, "lit").set_ephemeral();
|
|
||||||
|
|
||||||
blt::gp::operation_t op_area([](const rice_record& rice_data) {
|
|
||||||
return rice_data.area;
|
|
||||||
}, "area");
|
|
||||||
|
|
||||||
blt::gp::operation_t op_perimeter([](const rice_record& rice_data) {
|
|
||||||
return rice_data.perimeter;
|
|
||||||
}, "perimeter");
|
|
||||||
|
|
||||||
blt::gp::operation_t op_major_axis_length([](const rice_record& rice_data) {
|
|
||||||
return rice_data.major_axis_length;
|
|
||||||
}, "major_axis_length");
|
|
||||||
|
|
||||||
blt::gp::operation_t op_minor_axis_length([](const rice_record& rice_data) {
|
|
||||||
return rice_data.minor_axis_length;
|
|
||||||
}, "minor_axis_length");
|
|
||||||
|
|
||||||
blt::gp::operation_t op_eccentricity([](const rice_record& rice_data) {
|
|
||||||
return rice_data.eccentricity;
|
|
||||||
}, "eccentricity");
|
|
||||||
|
|
||||||
blt::gp::operation_t op_convex_area([](const rice_record& rice_data) {
|
|
||||||
return rice_data.convex_area;
|
|
||||||
}, "convex_area");
|
|
||||||
|
|
||||||
blt::gp::operation_t op_extent([](const rice_record& rice_data) {
|
|
||||||
return rice_data.extent;
|
|
||||||
}, "extent");
|
|
||||||
|
|
||||||
constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fitness_t& fitness, blt::size_t) {
|
|
||||||
for (auto& training_case : training_cases)
|
|
||||||
{
|
|
||||||
auto v = current_tree.get_evaluation_value<float>(&training_case);
|
|
||||||
switch (training_case.type)
|
|
||||||
{
|
|
||||||
case rice_type_t::Cammeo:
|
|
||||||
if (v >= 0)
|
|
||||||
fitness.hits++;
|
|
||||||
break;
|
|
||||||
case rice_type_t::Osmancik:
|
|
||||||
if (v < 0)
|
|
||||||
fitness.hits++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fitness.raw_fitness = static_cast<double>(fitness.hits);
|
|
||||||
fitness.standardized_fitness = fitness.raw_fitness;
|
|
||||||
fitness.adjusted_fitness = 1.0 - (1.0 / (1.0 + fitness.standardized_fitness));
|
|
||||||
return static_cast<blt::size_t>(fitness.hits) == training_cases.size();
|
|
||||||
};
|
|
||||||
|
|
||||||
void load_rice_data(std::string_view rice_file_path)
|
|
||||||
{
|
|
||||||
auto rice_file_data = blt::fs::getLinesFromFile(rice_file_path);
|
|
||||||
size_t index = 0;
|
|
||||||
while (!blt::string::contains(rice_file_data[index++], "@DATA"))
|
|
||||||
{}
|
|
||||||
std::vector<rice_record> c;
|
|
||||||
std::vector<rice_record> o;
|
|
||||||
for (std::string_view v : blt::itr_offset(rice_file_data, index))
|
|
||||||
{
|
|
||||||
auto data = blt::string::split(v, ',');
|
|
||||||
rice_record r{std::stof(data[0]), std::stof(data[1]), std::stof(data[2]), std::stof(data[3]), std::stof(data[4]), std::stof(data[5]),
|
|
||||||
std::stof(data[6]), blt::string::contains(data[7], "Cammeo") ? rice_type_t::Cammeo : rice_type_t::Osmancik};
|
|
||||||
switch (r.type)
|
|
||||||
{
|
|
||||||
case rice_type_t::Cammeo:
|
|
||||||
c.push_back(r);
|
|
||||||
break;
|
|
||||||
case rice_type_t::Osmancik:
|
|
||||||
o.push_back(r);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blt::size_t total_records = c.size() + o.size();
|
|
||||||
blt::size_t training_size = std::min(total_records / 3, 1000ul);
|
|
||||||
for (blt::size_t i = 0; i < training_size; i++)
|
|
||||||
{
|
|
||||||
auto& random = program.get_random();
|
|
||||||
auto& vec = random.choice() ? c : o;
|
|
||||||
auto pos = random.get_i64(0, static_cast<blt::i64>(vec.size()));
|
|
||||||
training_cases.push_back(vec[pos]);
|
|
||||||
vec.erase(vec.begin() + pos);
|
|
||||||
}
|
|
||||||
testing_cases.insert(testing_cases.end(), c.begin(), c.end());
|
|
||||||
testing_cases.insert(testing_cases.end(), o.begin(), o.end());
|
|
||||||
std::shuffle(testing_cases.begin(), testing_cases.end(), program.get_random());
|
|
||||||
BLT_INFO("Created training set of size %ld, testing set is of size %ld", training_size, testing_cases.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
struct test_results_t
|
|
||||||
{
|
|
||||||
blt::size_t cc = 0;
|
|
||||||
blt::size_t co = 0;
|
|
||||||
blt::size_t oo = 0;
|
|
||||||
blt::size_t oc = 0;
|
|
||||||
blt::size_t hits = 0;
|
|
||||||
blt::size_t size = 0;
|
|
||||||
double percent_hit = 0;
|
|
||||||
|
|
||||||
test_results_t& operator+=(const test_results_t& a)
|
|
||||||
{
|
|
||||||
cc += a.cc;
|
|
||||||
co += a.co;
|
|
||||||
oo += a.oo;
|
|
||||||
oc += a.oc;
|
|
||||||
hits += a.hits;
|
|
||||||
size += a.size;
|
|
||||||
percent_hit += a.percent_hit;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
test_results_t& operator/=(blt::size_t s)
|
|
||||||
{
|
|
||||||
cc /= s;
|
|
||||||
co /= s;
|
|
||||||
oo /= s;
|
|
||||||
oc /= s;
|
|
||||||
hits /= s;
|
|
||||||
size /= s;
|
|
||||||
percent_hit /= static_cast<double>(s);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend bool operator<(const test_results_t& a, const test_results_t& b)
|
|
||||||
{
|
|
||||||
return a.hits < b.hits;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend bool operator>(const test_results_t& a, const test_results_t& b)
|
|
||||||
{
|
|
||||||
return a.hits > b.hits;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
test_results_t test_individual(blt::gp::individual_t& i)
|
|
||||||
{
|
|
||||||
test_results_t results;
|
|
||||||
|
|
||||||
for (auto& testing_case : testing_cases)
|
|
||||||
{
|
|
||||||
auto result = i.tree.get_evaluation_value<float>(&testing_case);
|
|
||||||
switch (testing_case.type)
|
|
||||||
{
|
|
||||||
case rice_type_t::Cammeo:
|
|
||||||
if (result >= 0)
|
|
||||||
results.cc++; // cammeo cammeo
|
|
||||||
else
|
|
||||||
results.co++; // cammeo osmancik
|
|
||||||
break;
|
|
||||||
case rice_type_t::Osmancik:
|
|
||||||
if (result < 0)
|
|
||||||
results.oo++; // osmancik osmancik
|
|
||||||
else
|
|
||||||
results.oc++; // osmancik cammeo
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
results.hits = results.cc + results.oo;
|
|
||||||
results.size = testing_cases.size();
|
|
||||||
results.percent_hit = static_cast<double>(results.hits) / static_cast<double>(results.size) * 100;
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, const char** argv)
|
|
||||||
{
|
|
||||||
blt::arg_parse parser;
|
|
||||||
parser.addArgument(blt::arg_builder{"-f", "--file"}.setHelp("File for rice data. Should be in .arff format.").setRequired().build());
|
|
||||||
|
|
||||||
auto args = parser.parse_args(argc, argv);
|
|
||||||
|
|
||||||
if (!args.contains("file"))
|
|
||||||
{
|
|
||||||
BLT_WARN("Please provide path to file with -f or --file");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto rice_file_path = args.get<std::string>("file");
|
|
||||||
|
|
||||||
BLT_INFO("Starting BLT-GP Rice Classification Example");
|
|
||||||
BLT_START_INTERVAL("Rice Classification", "Main");
|
|
||||||
BLT_DEBUG("Setup Fitness cases");
|
|
||||||
load_rice_data(rice_file_path);
|
|
||||||
|
|
||||||
BLT_DEBUG("Setup Types and Operators");
|
|
||||||
|
|
||||||
blt::gp::operator_builder<rice_record> builder{};
|
|
||||||
program.set_operations(builder.build(add, sub, mul, pro_div, op_exp, op_log, lit, op_area, op_perimeter, op_major_axis_length,
|
|
||||||
op_minor_axis_length, op_eccentricity, op_convex_area, op_extent));
|
|
||||||
|
|
||||||
BLT_DEBUG("Generate Initial Population");
|
|
||||||
auto sel = blt::gp::select_tournament_t{};
|
|
||||||
program.generate_population(program.get_typesystem().get_type<float>().id(), fitness_function, sel, sel, sel);
|
|
||||||
|
|
||||||
BLT_DEBUG("Begin Generation Loop");
|
|
||||||
while (!program.should_terminate())
|
|
||||||
{
|
|
||||||
BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation());
|
|
||||||
BLT_TRACE("Creating next generation");
|
|
||||||
BLT_START_INTERVAL("Rice Classification", "Gen");
|
|
||||||
program.create_next_generation();
|
|
||||||
BLT_END_INTERVAL("Rice Classification", "Gen");
|
|
||||||
BLT_TRACE("Move to next generation");
|
|
||||||
BLT_START_INTERVAL("Rice Classification", "Fitness");
|
|
||||||
program.next_generation();
|
|
||||||
BLT_TRACE("Evaluate Fitness");
|
|
||||||
program.evaluate_fitness();
|
|
||||||
BLT_END_INTERVAL("Rice Classification", "Fitness");
|
|
||||||
auto& stats = program.get_population_stats();
|
|
||||||
BLT_TRACE("Stats:");
|
|
||||||
BLT_TRACE("Average fitness: %lf", stats.average_fitness.load());
|
|
||||||
BLT_TRACE("Best fitness: %lf", stats.best_fitness.load());
|
|
||||||
BLT_TRACE("Worst fitness: %lf", stats.worst_fitness.load());
|
|
||||||
BLT_TRACE("Overall fitness: %lf", stats.overall_fitness.load());
|
|
||||||
BLT_TRACE("----------------------------------------------");
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
BLT_END_INTERVAL("Rice Classification", "Main");
|
|
||||||
|
|
||||||
std::vector<std::pair<test_results_t, blt::gp::individual_t*>> results;
|
|
||||||
for (auto& i : program.get_current_pop().get_individuals())
|
|
||||||
results.emplace_back(test_individual(i), &i);
|
|
||||||
std::sort(results.begin(), results.end(), [](const auto& a, const auto& b) {
|
|
||||||
return a.first > b.first;
|
|
||||||
});
|
|
||||||
|
|
||||||
BLT_INFO("Best results:");
|
|
||||||
for (blt::size_t index = 0; index < 3; index++)
|
|
||||||
{
|
|
||||||
const auto& record = results[index].first;
|
|
||||||
const auto& i = *results[index].second;
|
|
||||||
|
|
||||||
BLT_INFO("Hits %ld, Total Cases %ld, Percent Hit: %lf", record.hits, record.size, record.percent_hit);
|
|
||||||
BLT_DEBUG("Cammeo Cammeo: %ld", record.cc);
|
|
||||||
BLT_DEBUG("Cammeo Osmancik: %ld", record.co);
|
|
||||||
BLT_DEBUG("Osmancik Osmancik: %ld", record.oo);
|
|
||||||
BLT_DEBUG("Osmancik Cammeo: %ld", record.oc);
|
|
||||||
BLT_DEBUG("Fitness: %lf, stand: %lf, raw: %lf", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness);
|
|
||||||
i.tree.print(program, std::cout);
|
|
||||||
|
|
||||||
std::cout << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
BLT_INFO("Worst Results:");
|
|
||||||
for (blt::size_t index = 0; index < 3; index++)
|
|
||||||
{
|
|
||||||
const auto& record = results[results.size() - 1 - index].first;
|
|
||||||
const auto& i = *results[results.size() - 1 - index].second;
|
|
||||||
|
|
||||||
BLT_INFO("Hits %ld, Total Cases %ld, Percent Hit: %lf", record.hits, record.size, record.percent_hit);
|
|
||||||
BLT_DEBUG("Cammeo Cammeo: %ld", record.cc);
|
|
||||||
BLT_DEBUG("Cammeo Osmancik: %ld", record.co);
|
|
||||||
BLT_DEBUG("Osmancik Osmancik: %ld", record.oo);
|
|
||||||
BLT_DEBUG("Osmancik Cammeo: %ld", record.oc);
|
|
||||||
BLT_DEBUG("Fitness: %lf, stand: %lf, raw: %lf", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness);
|
|
||||||
|
|
||||||
std::cout << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
BLT_INFO("Average Results");
|
|
||||||
test_results_t avg{};
|
|
||||||
for (const auto& v : results)
|
|
||||||
avg += v.first;
|
|
||||||
avg /= results.size();
|
|
||||||
BLT_INFO("Hits %ld, Total Cases %ld, Percent Hit: %lf", avg.hits, avg.size, avg.percent_hit);
|
|
||||||
BLT_DEBUG("Cammeo Cammeo: %ld", avg.cc);
|
|
||||||
BLT_DEBUG("Cammeo Osmancik: %ld", avg.co);
|
|
||||||
BLT_DEBUG("Osmancik Osmancik: %ld", avg.oo);
|
|
||||||
BLT_DEBUG("Osmancik Cammeo: %ld", avg.oc);
|
|
||||||
std::cout << "\n";
|
|
||||||
|
|
||||||
BLT_PRINT_PROFILE("Rice Classification", blt::PRINT_CYCLES | blt::PRINT_THREAD | blt::PRINT_WALL);
|
|
||||||
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
BLT_TRACE("Total Allocations: %ld times with a total of %s", blt::gp::tracker.getAllocations(),
|
|
||||||
blt::byte_convert_t(blt::gp::tracker.getAllocatedBytes()).convert_to_nearest_type().to_pretty_string().c_str());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLT_GP_EXAMPLES_RICE_CLASSIFICATION_H
|
||||||
|
#define BLT_GP_EXAMPLES_RICE_CLASSIFICATION_H
|
||||||
|
|
||||||
|
#include "examples_base.h"
|
||||||
|
|
||||||
|
namespace blt::gp::example
|
||||||
|
{
|
||||||
|
class rice_classification_t : public example_base_t
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
enum class rice_type_t
|
||||||
|
{
|
||||||
|
Cammeo,
|
||||||
|
Osmancik
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rice_record
|
||||||
|
{
|
||||||
|
float area;
|
||||||
|
float perimeter;
|
||||||
|
float major_axis_length;
|
||||||
|
float minor_axis_length;
|
||||||
|
float eccentricity;
|
||||||
|
float convex_area;
|
||||||
|
float extent;
|
||||||
|
rice_type_t type;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool fitness_function(const tree_t& current_tree, fitness_t& fitness, size_t) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename SEED>
|
||||||
|
rice_classification_t(SEED&& seed, const prog_config_t& config): example_base_t{std::forward<SEED>(seed), config}
|
||||||
|
{
|
||||||
|
BLT_INFO("Starting BLT-GP Rice Classification Example");
|
||||||
|
fitness_function_ref = [this](const tree_t& t, fitness_t& f, const size_t i)
|
||||||
|
{
|
||||||
|
return fitness_function(t, f, i);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_operators();
|
||||||
|
|
||||||
|
void load_rice_data(std::string_view rice_file_path);
|
||||||
|
|
||||||
|
[[nodiscard]] confusion_matrix_t test_individual(const individual_t& individual) const;
|
||||||
|
|
||||||
|
void execute(const std::string_view rice_file_path)
|
||||||
|
{
|
||||||
|
load_rice_data(rice_file_path);
|
||||||
|
make_operators();
|
||||||
|
generate_initial_population();
|
||||||
|
run_generation_loop();
|
||||||
|
evaluate_individuals();
|
||||||
|
print_best();
|
||||||
|
print_average();
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_generation_loop()
|
||||||
|
{
|
||||||
|
BLT_DEBUG("Begin Generation Loop");
|
||||||
|
while (!program.should_terminate())
|
||||||
|
{
|
||||||
|
BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation());
|
||||||
|
BLT_TRACE("Creating next generation");
|
||||||
|
program.create_next_generation();
|
||||||
|
BLT_TRACE("Move to next generation");
|
||||||
|
program.next_generation();
|
||||||
|
BLT_TRACE("Evaluate Fitness");
|
||||||
|
program.evaluate_fitness();
|
||||||
|
auto& stats = program.get_population_stats();
|
||||||
|
BLT_TRACE("Avg Fit: %lf, Best Fit: %lf, Worst Fit: %lf, Overall Fit: %lf",
|
||||||
|
stats.average_fitness.load(std::memory_order_relaxed), stats.best_fitness.load(std::memory_order_relaxed),
|
||||||
|
stats.worst_fitness.load(std::memory_order_relaxed), stats.overall_fitness.load(std::memory_order_relaxed));
|
||||||
|
BLT_TRACE("----------------------------------------------");
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void evaluate_individuals()
|
||||||
|
{
|
||||||
|
results.clear();
|
||||||
|
for (auto& i : program.get_current_pop().get_individuals())
|
||||||
|
results.emplace_back(test_individual(i), &i);
|
||||||
|
std::sort(results.begin(), results.end(), [](const auto& a, const auto& b)
|
||||||
|
{
|
||||||
|
return a.first > b.first;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_initial_population()
|
||||||
|
{
|
||||||
|
BLT_DEBUG("Generate Initial Population");
|
||||||
|
static auto sel = select_tournament_t{};
|
||||||
|
if (crossover_sel == nullptr)
|
||||||
|
crossover_sel = &sel;
|
||||||
|
if (mutation_sel == nullptr)
|
||||||
|
mutation_sel = &sel;
|
||||||
|
if (reproduction_sel == nullptr)
|
||||||
|
reproduction_sel = &sel;
|
||||||
|
program.generate_initial_population(program.get_typesystem().get_type<float>().id());
|
||||||
|
program.setup_generational_evaluation(fitness_function_ref, *crossover_sel, *mutation_sel, *reproduction_sel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_best(const size_t amount = 3)
|
||||||
|
{
|
||||||
|
BLT_INFO("Best results:");
|
||||||
|
for (size_t index = 0; index < amount; index++)
|
||||||
|
{
|
||||||
|
const auto& record = results[index].first;
|
||||||
|
const auto& i = *results[index].second;
|
||||||
|
|
||||||
|
BLT_INFO("Hits %ld, Total Cases %ld, Percent Hit: %lf", record.get_hits(), record.get_total(), record.get_percent_hit());
|
||||||
|
std::cout << record.pretty_print() << std::endl;
|
||||||
|
BLT_DEBUG("Fitness: %lf, stand: %lf, raw: %lf", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness);
|
||||||
|
i.tree.print(std::cout);
|
||||||
|
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_worst(const size_t amount = 3) const
|
||||||
|
{
|
||||||
|
BLT_INFO("Worst Results:");
|
||||||
|
for (size_t index = 0; index < amount; index++)
|
||||||
|
{
|
||||||
|
const auto& record = results[results.size() - 1 - index].first;
|
||||||
|
const auto& i = *results[results.size() - 1 - index].second;
|
||||||
|
|
||||||
|
BLT_INFO("Hits %ld, Total Cases %ld, Percent Hit: %lf", record.get_hits(), record.get_total(), record.get_percent_hit());
|
||||||
|
std::cout << record.pretty_print() << std::endl;
|
||||||
|
BLT_DEBUG("Fitness: %lf, stand: %lf, raw: %lf", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness);
|
||||||
|
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_average()
|
||||||
|
{
|
||||||
|
BLT_INFO("Average Results");
|
||||||
|
confusion_matrix_t avg{};
|
||||||
|
avg.set_name_a("cammeo");
|
||||||
|
avg.set_name_b("osmancik");
|
||||||
|
for (const auto& [matrix, _] : results)
|
||||||
|
avg += matrix;
|
||||||
|
avg /= results.size();
|
||||||
|
BLT_INFO("Hits %ld, Total Cases %ld, Percent Hit: %lf", avg.get_hits(), avg.get_total(), avg.get_percent_hit());
|
||||||
|
std::cout << avg.pretty_print() << std::endl;
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& get_results() { return results; }
|
||||||
|
const auto& get_results() const { return results; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<rice_record> training_cases;
|
||||||
|
std::vector<rice_record> testing_cases;
|
||||||
|
std::vector<std::pair<confusion_matrix_t, individual_t*>> results;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //BLT_GP_EXAMPLES_RICE_CLASSIFICATION_H
|
|
@ -0,0 +1,224 @@
|
||||||
|
/*
|
||||||
|
* This rice classification example uses data from the UC Irvine Machine Learning repository.
|
||||||
|
* The data for this example can be found at:
|
||||||
|
* https://archive.ics.uci.edu/dataset/545/rice+cammeo+and+osmancik
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <blt/gp/program.h>
|
||||||
|
#include <blt/profiling/profiler_v2.h>
|
||||||
|
#include <blt/gp/tree.h>
|
||||||
|
#include <blt/logging/logging.h>
|
||||||
|
#include <blt/format/format.h>
|
||||||
|
#include <blt/parse/argparse.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include "../rice_classification.h"
|
||||||
|
#include "blt/fs/loader.h"
|
||||||
|
|
||||||
|
static const auto SEED_FUNC = [] { return std::random_device()(); };
|
||||||
|
|
||||||
|
blt::gp::prog_config_t config = blt::gp::prog_config_t()
|
||||||
|
.set_initial_min_tree_size(2)
|
||||||
|
.set_initial_max_tree_size(6)
|
||||||
|
.set_elite_count(2)
|
||||||
|
.set_crossover_chance(0.8)
|
||||||
|
.set_mutation_chance(0.1)
|
||||||
|
.set_reproduction_chance(0)
|
||||||
|
.set_max_generations(50)
|
||||||
|
.set_pop_size(500)
|
||||||
|
.set_thread_count(0);
|
||||||
|
|
||||||
|
int main(int argc, const char** argv)
|
||||||
|
{
|
||||||
|
blt::arg_parse parser;
|
||||||
|
parser.addArgument(blt::arg_builder{"file"}
|
||||||
|
.setHelp("File for rice data. Should be in .arff format.").setDefault("../datasets/Rice_Cammeo_Osmancik.arff").build());
|
||||||
|
|
||||||
|
auto args = parser.parse_args(argc, argv);
|
||||||
|
|
||||||
|
if (!args.contains("file"))
|
||||||
|
{
|
||||||
|
BLT_WARN("Please provide path to file with -f or --file");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rice_file_path = args.get<std::string>("file");
|
||||||
|
|
||||||
|
blt::gp::example::rice_classification_t rice_classification{SEED_FUNC, config};
|
||||||
|
|
||||||
|
rice_classification.execute(rice_file_path);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blt::gp::example::rice_classification_t::make_operators()
|
||||||
|
{
|
||||||
|
BLT_DEBUG("Setup Types and Operators");
|
||||||
|
static operation_t add{[](const float a, const float b) { return a + b; }, "add"};
|
||||||
|
static operation_t sub([](const float a, const float b) { return a - b; }, "sub");
|
||||||
|
static operation_t mul([](const float a, const float b) { return a * b; }, "mul");
|
||||||
|
static operation_t pro_div([](const float a, const float b) { return b == 0.0f ? 0.0f : a / b; }, "div");
|
||||||
|
static operation_t op_exp([](const float a) { return std::exp(a); }, "exp");
|
||||||
|
static operation_t op_log([](const float a) { return a <= 0.0f ? 0.0f : std::log(a); }, "log");
|
||||||
|
static auto lit = operation_t([this]()
|
||||||
|
{
|
||||||
|
return program.get_random().get_float(-32000.0f, 32000.0f);
|
||||||
|
}, "lit").set_ephemeral();
|
||||||
|
|
||||||
|
static operation_t op_area([](const rice_record& rice_data)
|
||||||
|
{
|
||||||
|
return rice_data.area;
|
||||||
|
}, "area");
|
||||||
|
|
||||||
|
static operation_t op_perimeter([](const rice_record& rice_data)
|
||||||
|
{
|
||||||
|
return rice_data.perimeter;
|
||||||
|
}, "perimeter");
|
||||||
|
|
||||||
|
static operation_t op_major_axis_length([](const rice_record& rice_data)
|
||||||
|
{
|
||||||
|
return rice_data.major_axis_length;
|
||||||
|
}, "major_axis_length");
|
||||||
|
|
||||||
|
static operation_t op_minor_axis_length([](const rice_record& rice_data)
|
||||||
|
{
|
||||||
|
return rice_data.minor_axis_length;
|
||||||
|
}, "minor_axis_length");
|
||||||
|
|
||||||
|
static operation_t op_eccentricity([](const rice_record& rice_data)
|
||||||
|
{
|
||||||
|
return rice_data.eccentricity;
|
||||||
|
}, "eccentricity");
|
||||||
|
|
||||||
|
static operation_t op_convex_area([](const rice_record& rice_data)
|
||||||
|
{
|
||||||
|
return rice_data.convex_area;
|
||||||
|
}, "convex_area");
|
||||||
|
|
||||||
|
static operation_t op_extent([](const rice_record& rice_data)
|
||||||
|
{
|
||||||
|
return rice_data.extent;
|
||||||
|
}, "extent");
|
||||||
|
|
||||||
|
operator_builder<rice_record> builder{};
|
||||||
|
builder.build(add, sub, mul, pro_div, op_exp, op_log, lit, op_area, op_perimeter, op_major_axis_length,
|
||||||
|
op_minor_axis_length, op_eccentricity, op_convex_area, op_extent);
|
||||||
|
program.set_operations(builder.grab());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blt::gp::example::rice_classification_t::fitness_function(const tree_t& current_tree, fitness_t& fitness, size_t) const
|
||||||
|
{
|
||||||
|
for (auto& training_case : training_cases)
|
||||||
|
{
|
||||||
|
BLT_GP_UPDATE_CONTEXT(training_case);
|
||||||
|
const auto v = current_tree.get_evaluation_value<float>(training_case);
|
||||||
|
switch (training_case.type)
|
||||||
|
{
|
||||||
|
case rice_type_t::Cammeo:
|
||||||
|
if (v >= 0)
|
||||||
|
fitness.hits++;
|
||||||
|
break;
|
||||||
|
case rice_type_t::Osmancik:
|
||||||
|
if (v < 0)
|
||||||
|
fitness.hits++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fitness.raw_fitness = static_cast<double>(fitness.hits);
|
||||||
|
fitness.standardized_fitness = fitness.raw_fitness;
|
||||||
|
// fitness.adjusted_fitness = 1.0 - (1.0 / (1.0 + fitness.standardized_fitness));
|
||||||
|
fitness.adjusted_fitness = fitness.standardized_fitness / static_cast<double>(training_cases.size());
|
||||||
|
return static_cast<size_t>(fitness.hits) == training_cases.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void blt::gp::example::rice_classification_t::load_rice_data(const std::string_view rice_file_path)
|
||||||
|
{
|
||||||
|
if (!std::filesystem::exists(rice_file_path))
|
||||||
|
{
|
||||||
|
BLT_WARN("Rice file not found!");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
BLT_DEBUG("Setup Fitness cases");
|
||||||
|
auto rice_file_data = fs::getLinesFromFile(rice_file_path);
|
||||||
|
size_t index = 0;
|
||||||
|
while (!string::contains(rice_file_data[index++], "@DATA"))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
std::vector<rice_record> c;
|
||||||
|
std::vector<rice_record> o;
|
||||||
|
for (const std::string_view v : iterate(rice_file_data).skip(index))
|
||||||
|
{
|
||||||
|
auto data = string::split(v, ',');
|
||||||
|
rice_record r{
|
||||||
|
std::stof(data[0]), std::stof(data[1]), std::stof(data[2]), std::stof(data[3]), std::stof(data[4]), std::stof(data[5]),
|
||||||
|
std::stof(data[6]), string::contains(data[7], "Cammeo") ? rice_type_t::Cammeo : rice_type_t::Osmancik
|
||||||
|
};
|
||||||
|
switch (r.type)
|
||||||
|
{
|
||||||
|
case rice_type_t::Cammeo:
|
||||||
|
c.push_back(r);
|
||||||
|
break;
|
||||||
|
case rice_type_t::Osmancik:
|
||||||
|
o.push_back(r);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t total_records = c.size() + o.size();
|
||||||
|
const size_t testing_size = total_records / 3;
|
||||||
|
for (size_t i = 0; i < testing_size; i++)
|
||||||
|
{
|
||||||
|
auto& random = program.get_random();
|
||||||
|
auto& vec = random.choice() ? c : o;
|
||||||
|
const auto pos = random.get_i64(0, static_cast<i64>(vec.size()));
|
||||||
|
testing_cases.push_back(vec[pos]);
|
||||||
|
vec.erase(vec.begin() + pos);
|
||||||
|
}
|
||||||
|
training_cases.insert(training_cases.end(), c.begin(), c.end());
|
||||||
|
training_cases.insert(training_cases.end(), o.begin(), o.end());
|
||||||
|
std::shuffle(training_cases.begin(), training_cases.end(), program.get_random());
|
||||||
|
BLT_INFO("Created testing set of size {}, training set is of size {}", testing_cases.size(), training_cases.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
blt::gp::confusion_matrix_t blt::gp::example::rice_classification_t::test_individual(const individual_t& individual) const
|
||||||
|
{
|
||||||
|
confusion_matrix_t confusion_matrix;
|
||||||
|
confusion_matrix.set_name_a("cammeo");
|
||||||
|
confusion_matrix.set_name_b("osmancik");
|
||||||
|
|
||||||
|
for (auto& testing_case : testing_cases)
|
||||||
|
{
|
||||||
|
const auto result = individual.tree.get_evaluation_value<float>(testing_case);
|
||||||
|
switch (testing_case.type)
|
||||||
|
{
|
||||||
|
case rice_type_t::Cammeo:
|
||||||
|
if (result >= 0)
|
||||||
|
confusion_matrix.is_A_predicted_A(); // cammeo cammeo
|
||||||
|
else
|
||||||
|
confusion_matrix.is_A_predicted_B(); // cammeo osmancik
|
||||||
|
break;
|
||||||
|
case rice_type_t::Osmancik:
|
||||||
|
if (result < 0)
|
||||||
|
confusion_matrix.is_B_predicted_B(); // osmancik osmancik
|
||||||
|
else
|
||||||
|
confusion_matrix.is_B_predicted_A(); // osmancik cammeo
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return confusion_matrix;
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* <Short Description>
|
||||||
|
* Copyright (C) 2024 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "../symbolic_regression.h"
|
||||||
|
|
||||||
|
// you can either use a straight numeric seed, or provide a function which produces a u64 output which will initialize the thread local random number generators.
|
||||||
|
static const auto SEED_FUNC = [] { return std::random_device()(); };
|
||||||
|
|
||||||
|
blt::gp::grow_generator_t grow_generator;
|
||||||
|
blt::gp::full_generator_t full_generator;
|
||||||
|
|
||||||
|
blt::gp::ramped_half_initializer_t ramped_half_initializer;
|
||||||
|
blt::gp::full_initializer_t full_initializer;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// config options can be chained together to form compound statements.
|
||||||
|
blt::gp::prog_config_t config = blt::gp::prog_config_t()
|
||||||
|
.set_initial_min_tree_size(2)
|
||||||
|
.set_initial_max_tree_size(6)
|
||||||
|
.set_elite_count(2)
|
||||||
|
.set_crossover_chance(0.9)
|
||||||
|
.set_mutation_chance(0.1)
|
||||||
|
.set_reproduction_chance(0.0)
|
||||||
|
.set_max_generations(50)
|
||||||
|
.set_pop_size(500)
|
||||||
|
.set_thread_count(16);
|
||||||
|
|
||||||
|
// example on how you can change the mutation config
|
||||||
|
blt::gp::mutation_t::config_t mut_config{};
|
||||||
|
mut_config.generator = grow_generator;
|
||||||
|
mut_config.replacement_min_depth = 2;
|
||||||
|
mut_config.replacement_max_depth = 6;
|
||||||
|
|
||||||
|
blt::gp::advanced_mutation_t mut_adv{mut_config};
|
||||||
|
blt::gp::mutation_t mut{mut_config};
|
||||||
|
|
||||||
|
// you can choose to set any type of system used by the GP. Mutation, Crossover, and Initializers
|
||||||
|
// (config options changed do not affect others, so you can programmatically change them at runtime)
|
||||||
|
config.set_initializer(ramped_half_initializer);
|
||||||
|
config.set_mutation(mut_adv);
|
||||||
|
|
||||||
|
// the config is copied into the gp_system so changing the config will not change the runtime of the program.
|
||||||
|
blt::gp::example::symbolic_regression_t regression{SEED_FUNC, config};
|
||||||
|
|
||||||
|
regression.execute();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -1,194 +0,0 @@
|
||||||
/*
|
|
||||||
* <Short Description>
|
|
||||||
* Copyright (C) 2024 Brett Terpstra
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#include <blt/gp/program.h>
|
|
||||||
#include <blt/profiling/profiler_v2.h>
|
|
||||||
#include <blt/gp/tree.h>
|
|
||||||
#include <blt/std/logging.h>
|
|
||||||
#include <blt/std/format.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include "operations_common.h"
|
|
||||||
#include "blt/math/averages.h"
|
|
||||||
|
|
||||||
//static constexpr long SEED = 41912;
|
|
||||||
static const unsigned long SEED = std::random_device()();
|
|
||||||
|
|
||||||
struct context
|
|
||||||
{
|
|
||||||
float x, y;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::array<context, 200> training_cases;
|
|
||||||
|
|
||||||
blt::gp::prog_config_t config = blt::gp::prog_config_t()
|
|
||||||
.set_initial_min_tree_size(2)
|
|
||||||
.set_initial_max_tree_size(6)
|
|
||||||
.set_elite_count(200)
|
|
||||||
.set_crossover_chance(0.9)
|
|
||||||
.set_mutation_chance(0.1)
|
|
||||||
.set_reproduction_chance(0)
|
|
||||||
.set_max_generations(50)
|
|
||||||
.set_pop_size(20000)
|
|
||||||
.set_thread_count(0);
|
|
||||||
//blt::gp::prog_config_t config = blt::gp::prog_config_t()
|
|
||||||
// .set_initial_min_tree_size(2)
|
|
||||||
// .set_initial_max_tree_size(6)
|
|
||||||
// .set_elite_count(2)
|
|
||||||
// .set_crossover_chance(0.9)
|
|
||||||
// .set_mutation_chance(0.1)
|
|
||||||
// .set_reproduction_chance(0)
|
|
||||||
// .set_max_generations(50)
|
|
||||||
// .set_pop_size(500)
|
|
||||||
// .set_thread_count(0);
|
|
||||||
|
|
||||||
blt::gp::gp_program program{SEED, config};
|
|
||||||
|
|
||||||
auto lit = blt::gp::operation_t([]() {
|
|
||||||
return program.get_random().get_float(-1.0f, 1.0f);
|
|
||||||
}, "lit").set_ephemeral();
|
|
||||||
|
|
||||||
blt::gp::operation_t op_x([](const context& context) {
|
|
||||||
return context.x;
|
|
||||||
}, "x");
|
|
||||||
|
|
||||||
constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fitness_t& fitness, blt::size_t) {
|
|
||||||
constexpr double value_cutoff = 1.e15;
|
|
||||||
for (auto& fitness_case : training_cases)
|
|
||||||
{
|
|
||||||
auto diff = std::abs(fitness_case.y - current_tree.get_evaluation_value<float>(&fitness_case));
|
|
||||||
if (diff < value_cutoff)
|
|
||||||
{
|
|
||||||
fitness.raw_fitness += diff;
|
|
||||||
if (diff < 0.01)
|
|
||||||
fitness.hits++;
|
|
||||||
} else
|
|
||||||
fitness.raw_fitness += value_cutoff;
|
|
||||||
}
|
|
||||||
fitness.standardized_fitness = fitness.raw_fitness;
|
|
||||||
fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness));
|
|
||||||
return static_cast<blt::size_t>(fitness.hits) == training_cases.size();
|
|
||||||
};
|
|
||||||
|
|
||||||
float example_function(float x)
|
|
||||||
{
|
|
||||||
return x * x * x * x + x * x * x + x * x + x;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
BLT_INFO("Starting BLT-GP Symbolic Regression Example");
|
|
||||||
BLT_START_INTERVAL("Symbolic Regression", "Main");
|
|
||||||
BLT_DEBUG("Setup Fitness cases");
|
|
||||||
for (auto& fitness_case : training_cases)
|
|
||||||
{
|
|
||||||
constexpr float range = 10;
|
|
||||||
constexpr float half_range = range / 2.0;
|
|
||||||
auto x = program.get_random().get_float(-half_range, half_range);
|
|
||||||
auto y = example_function(x);
|
|
||||||
fitness_case = {x, y};
|
|
||||||
}
|
|
||||||
|
|
||||||
BLT_DEBUG("Setup Types and Operators");
|
|
||||||
blt::gp::operator_builder<context> builder{};
|
|
||||||
program.set_operations(builder.build(add, sub, mul, pro_div, op_sin, op_cos, op_exp, op_log, lit, op_x));
|
|
||||||
|
|
||||||
BLT_DEBUG("Generate Initial Population");
|
|
||||||
auto sel = blt::gp::select_tournament_t{};
|
|
||||||
program.generate_population(program.get_typesystem().get_type<float>().id(), fitness_function, sel, sel, sel);
|
|
||||||
|
|
||||||
BLT_DEBUG("Begin Generation Loop");
|
|
||||||
while (!program.should_terminate())
|
|
||||||
{
|
|
||||||
BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation());
|
|
||||||
BLT_TRACE("Creating next generation");
|
|
||||||
BLT_START_INTERVAL("Symbolic Regression", "Gen");
|
|
||||||
program.create_next_generation();
|
|
||||||
BLT_END_INTERVAL("Symbolic Regression", "Gen");
|
|
||||||
BLT_TRACE("Move to next generation");
|
|
||||||
BLT_START_INTERVAL("Symbolic Regression", "Fitness");
|
|
||||||
program.next_generation();
|
|
||||||
BLT_TRACE("Evaluate Fitness");
|
|
||||||
program.evaluate_fitness();
|
|
||||||
BLT_END_INTERVAL("Symbolic Regression", "Fitness");
|
|
||||||
BLT_TRACE("----------------------------------------------");
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
BLT_END_INTERVAL("Symbolic Regression", "Main");
|
|
||||||
|
|
||||||
auto best = program.get_best_individuals<3>();
|
|
||||||
|
|
||||||
BLT_INFO("Best approximations:");
|
|
||||||
for (auto& i_ref : best)
|
|
||||||
{
|
|
||||||
auto& i = i_ref.get();
|
|
||||||
BLT_DEBUG("Fitness: %lf, stand: %lf, raw: %lf", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness);
|
|
||||||
i.tree.print(program, std::cout);
|
|
||||||
std::cout << "\n";
|
|
||||||
}
|
|
||||||
auto& stats = program.get_population_stats();
|
|
||||||
BLT_INFO("Stats:");
|
|
||||||
BLT_INFO("Average fitness: %lf", stats.average_fitness.load());
|
|
||||||
BLT_INFO("Best fitness: %lf", stats.best_fitness.load());
|
|
||||||
BLT_INFO("Worst fitness: %lf", stats.worst_fitness.load());
|
|
||||||
BLT_INFO("Overall fitness: %lf", stats.overall_fitness.load());
|
|
||||||
// TODO: make stats helper
|
|
||||||
|
|
||||||
BLT_PRINT_PROFILE("Symbolic Regression", blt::PRINT_CYCLES | blt::PRINT_THREAD | blt::PRINT_WALL);
|
|
||||||
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
BLT_TRACE("Total Allocations: %ld times with a total of %s, peak allocated bytes %s", blt::gp::tracker.getAllocations(),
|
|
||||||
blt::byte_convert_t(blt::gp::tracker.getAllocatedBytes()).convert_to_nearest_type().to_pretty_string().c_str(),
|
|
||||||
blt::byte_convert_t(blt::gp::tracker.getPeakAllocatedBytes()).convert_to_nearest_type().to_pretty_string().c_str());
|
|
||||||
BLT_TRACE("------------------------------------------------------");
|
|
||||||
auto evaluation_calls_v = blt::gp::evaluation_calls.get_calls();
|
|
||||||
auto evaluation_allocations_v = blt::gp::evaluation_allocations.get_calls();
|
|
||||||
BLT_TRACE("Total Evaluation Calls: %ld; Peak Bytes Allocated %s", evaluation_calls_v,
|
|
||||||
blt::string::bytes_to_pretty(blt::gp::evaluation_calls.get_value()).c_str());
|
|
||||||
BLT_TRACE("Total Evaluation Allocations: %ld; Bytes %s; Average %s", evaluation_allocations_v,
|
|
||||||
blt::string::bytes_to_pretty(blt::gp::evaluation_allocations.get_value()).c_str(),
|
|
||||||
blt::string::bytes_to_pretty(blt::average(blt::gp::evaluation_allocations.get_value(), evaluation_allocations_v)).c_str());
|
|
||||||
BLT_TRACE("Percent Evaluation calls allocate? %lf%%", blt::average(evaluation_allocations_v, evaluation_calls_v) * 100);
|
|
||||||
BLT_TRACE("------------------------------------------------------");
|
|
||||||
auto crossover_calls_v = blt::gp::crossover_calls.get_calls();
|
|
||||||
auto crossover_allocations_v = blt::gp::crossover_allocations.get_calls();
|
|
||||||
auto mutation_calls_v = blt::gp::mutation_calls.get_calls();
|
|
||||||
auto mutation_allocations_v = blt::gp::mutation_allocations.get_calls();
|
|
||||||
auto reproduction_calls_v = blt::gp::reproduction_calls.get_calls();
|
|
||||||
auto reproduction_allocations_v = blt::gp::reproduction_allocations.get_calls();
|
|
||||||
BLT_TRACE("Total Crossover Calls: %ld; Peak Bytes Allocated %s", crossover_calls_v,
|
|
||||||
blt::string::bytes_to_pretty(blt::gp::crossover_calls.get_value()).c_str());
|
|
||||||
BLT_TRACE("Total Mutation Calls: %ld; Peak Bytes Allocated %s", mutation_calls_v,
|
|
||||||
blt::string::bytes_to_pretty(blt::gp::mutation_calls.get_value()).c_str());
|
|
||||||
BLT_TRACE("Total Reproduction Calls: %ld; Peak Bytes Allocated %s", reproduction_calls_v,
|
|
||||||
blt::string::bytes_to_pretty(blt::gp::reproduction_calls.get_value()).c_str());
|
|
||||||
BLT_TRACE("Total Crossover Allocations: %ld; Bytes %s; Average %s", crossover_allocations_v,
|
|
||||||
blt::string::bytes_to_pretty(blt::gp::crossover_allocations.get_value()).c_str(),
|
|
||||||
blt::string::bytes_to_pretty(blt::average(blt::gp::crossover_allocations.get_value(), crossover_allocations_v)).c_str());
|
|
||||||
BLT_TRACE("Total Mutation Allocations: %ld; Bytes %s; Average %s", mutation_allocations_v,
|
|
||||||
blt::string::bytes_to_pretty(blt::gp::mutation_allocations.get_value()).c_str(),
|
|
||||||
blt::string::bytes_to_pretty(blt::average(blt::gp::mutation_allocations.get_value(), mutation_allocations_v)).c_str());
|
|
||||||
BLT_TRACE("Total Reproduction Allocations: %ld; Bytes %s; Average %s", reproduction_allocations_v,
|
|
||||||
blt::string::bytes_to_pretty(blt::gp::reproduction_allocations.get_value()).c_str(),
|
|
||||||
blt::string::bytes_to_pretty(blt::average(blt::gp::reproduction_allocations.get_value(), reproduction_allocations_v)).c_str());
|
|
||||||
BLT_TRACE("Percent Crossover calls allocate? %lf%%", blt::average(crossover_allocations_v, crossover_calls_v) * 100);
|
|
||||||
BLT_TRACE("Percent Mutation calls allocate? %lf%%", blt::average(mutation_allocations_v, mutation_calls_v) * 100);
|
|
||||||
BLT_TRACE("Percent Reproduction calls allocate? %lf%%", blt::average(reproduction_allocations_v, reproduction_calls_v) * 100);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -0,0 +1,227 @@
|
||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLT_GP_EXAMPLE_SYMBOLIC_REGRESSION_H
|
||||||
|
#define BLT_GP_EXAMPLE_SYMBOLIC_REGRESSION_H
|
||||||
|
|
||||||
|
#include "examples_base.h"
|
||||||
|
#include <blt/logging/logging.h>
|
||||||
|
#include <blt/format/format.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace blt::gp::example
|
||||||
|
{
|
||||||
|
class symbolic_regression_t : public example_base_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct context
|
||||||
|
{
|
||||||
|
float x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool fitness_function(const tree_t& current_tree, fitness_t& fitness, size_t) const
|
||||||
|
{
|
||||||
|
constexpr static double value_cutoff = 1.e15;
|
||||||
|
for (auto& fitness_case : training_cases)
|
||||||
|
{
|
||||||
|
BLT_GP_UPDATE_CONTEXT(fitness_case);
|
||||||
|
const auto diff = std::abs(fitness_case.y - current_tree.get_evaluation_value<float>(fitness_case));
|
||||||
|
if (diff < value_cutoff)
|
||||||
|
{
|
||||||
|
fitness.raw_fitness += diff;
|
||||||
|
if (diff <= 0.01)
|
||||||
|
fitness.hits++;
|
||||||
|
} else
|
||||||
|
fitness.raw_fitness += value_cutoff;
|
||||||
|
}
|
||||||
|
fitness.standardized_fitness = fitness.raw_fitness;
|
||||||
|
fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness));
|
||||||
|
return static_cast<size_t>(fitness.hits) == training_cases.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static float example_function(const float x)
|
||||||
|
{
|
||||||
|
return x * x * x * x + x * x * x + x * x + x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename SEED>
|
||||||
|
symbolic_regression_t(SEED seed, const prog_config_t& config): example_base_t{std::forward<SEED>(seed), config}
|
||||||
|
{
|
||||||
|
BLT_INFO("Starting BLT-GP Symbolic Regression Example");
|
||||||
|
BLT_DEBUG("Setup Fitness cases");
|
||||||
|
for (auto& fitness_case : training_cases)
|
||||||
|
{
|
||||||
|
constexpr float range = 10;
|
||||||
|
constexpr float half_range = range / 2.0;
|
||||||
|
const auto x = program.get_random().get_float(-half_range, half_range);
|
||||||
|
const auto y = example_function(x);
|
||||||
|
fitness_case = {x, y};
|
||||||
|
}
|
||||||
|
|
||||||
|
fitness_function_ref = [this](const tree_t& t, fitness_t& f, const size_t i) {
|
||||||
|
return fitness_function(t, f, i);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_operations()
|
||||||
|
{
|
||||||
|
BLT_DEBUG("Setup Types and Operators");
|
||||||
|
static operation_t add{
|
||||||
|
// this is the function used by the operation
|
||||||
|
[](const float a, const float b) {
|
||||||
|
return a + b;
|
||||||
|
},
|
||||||
|
// this name is optional and is used if you print an individual
|
||||||
|
"add"
|
||||||
|
};
|
||||||
|
static operation_t sub([](const float a, const float b) {
|
||||||
|
return a - b;
|
||||||
|
}, "sub");
|
||||||
|
static operation_t mul([](const float a, const float b) {
|
||||||
|
return a * b;
|
||||||
|
}, "mul");
|
||||||
|
static operation_t pro_div([](const float a, const float b) {
|
||||||
|
return b == 0.0f ? 0.0f : a / b;
|
||||||
|
}, "div");
|
||||||
|
static operation_t op_sin([](const float a) {
|
||||||
|
return std::sin(a);
|
||||||
|
}, "sin");
|
||||||
|
static operation_t op_cos([](const float a) {
|
||||||
|
return std::cos(a);
|
||||||
|
}, "cos");
|
||||||
|
static operation_t op_exp([](const float a) {
|
||||||
|
return std::exp(a);
|
||||||
|
}, "exp");
|
||||||
|
static operation_t op_log([](const float a) {
|
||||||
|
return a <= 0.0f ? 0.0f : std::log(a);
|
||||||
|
}, "log");
|
||||||
|
static auto lit = operation_t([this]() {
|
||||||
|
return program.get_random().get_float(-1.0f, 1.0f);
|
||||||
|
}, "lit").set_ephemeral();
|
||||||
|
|
||||||
|
static operation_t op_x([](const context& context) {
|
||||||
|
return context.x;
|
||||||
|
}, "x");
|
||||||
|
|
||||||
|
operator_builder<context> builder{};
|
||||||
|
builder.build(add, sub, mul, pro_div, op_sin, op_cos, op_exp, op_log, lit, op_x);
|
||||||
|
program.set_operations(builder.grab());
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_initial_population()
|
||||||
|
{
|
||||||
|
BLT_DEBUG("Generate Initial Population");
|
||||||
|
static auto sel = select_tournament_t{};
|
||||||
|
if (crossover_sel == nullptr)
|
||||||
|
crossover_sel = &sel;
|
||||||
|
if (mutation_sel == nullptr)
|
||||||
|
mutation_sel = &sel;
|
||||||
|
if (reproduction_sel == nullptr)
|
||||||
|
reproduction_sel = &sel;
|
||||||
|
program.generate_initial_population(program.get_typesystem().get_type<float>().id());
|
||||||
|
program.setup_generational_evaluation(fitness_function_ref, *crossover_sel,
|
||||||
|
*mutation_sel, *reproduction_sel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_generation_loop()
|
||||||
|
{
|
||||||
|
BLT_DEBUG("Begin Generation Loop");
|
||||||
|
while (!program.should_terminate())
|
||||||
|
{
|
||||||
|
#ifdef BLT_TRACK_ALLOCATIONS
|
||||||
|
auto cross = crossover_calls.start_measurement();
|
||||||
|
auto mut = mutation_calls.start_measurement();
|
||||||
|
auto repo = reproduction_calls.start_measurement();
|
||||||
|
#endif
|
||||||
|
BLT_TRACE("------------\\{Begin Generation {}}------------", program.get_current_generation());
|
||||||
|
BLT_TRACE("Creating next generation");
|
||||||
|
program.create_next_generation();
|
||||||
|
BLT_TRACE("Move to next generation");
|
||||||
|
program.next_generation();
|
||||||
|
BLT_TRACE("Evaluate Fitness");
|
||||||
|
program.evaluate_fitness();
|
||||||
|
const auto& stats = program.get_population_stats();
|
||||||
|
BLT_TRACE("Avg Fit: {:0.6f}, Best Fit: {:0.6f}, Worst Fit: {:0.6f}, Overall Fit: {:0.6f}", stats.average_fitness.load(std::memory_order_relaxed),
|
||||||
|
stats.best_fitness.load(std::memory_order_relaxed), stats.worst_fitness.load(std::memory_order_relaxed),
|
||||||
|
stats.overall_fitness.load(std::memory_order_relaxed));
|
||||||
|
#ifdef BLT_TRACK_ALLOCATIONS
|
||||||
|
crossover_calls.stop_measurement(cross);
|
||||||
|
mutation_calls.stop_measurement(mut);
|
||||||
|
reproduction_calls.stop_measurement(repo);
|
||||||
|
const auto total = (cross.get_call_difference() * 2) + mut.get_call_difference() + repo.get_call_difference();
|
||||||
|
BLT_TRACE("Calls Crossover: {}, Mutation {}, Reproduction {}; {}", cross.get_call_difference(), mut.get_call_difference(), repo.get_call_difference(), total);
|
||||||
|
BLT_TRACE("Value Crossover: {}, Mutation {}, Reproduction {}; {}", cross.get_value_difference(), mut.get_value_difference(), repo.get_value_difference(), (cross.get_value_difference() * 2 + mut.get_value_difference() + repo.get_value_difference()) - total);
|
||||||
|
#endif
|
||||||
|
BLT_TRACE("----------------------------------------------");
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto get_and_print_best()
|
||||||
|
{
|
||||||
|
const auto best = program.get_best_individuals<3>();
|
||||||
|
|
||||||
|
BLT_INFO("Best approximations:");
|
||||||
|
for (auto& i_ref : best)
|
||||||
|
{
|
||||||
|
auto& i = i_ref.get();
|
||||||
|
BLT_DEBUG("Fitness: {:0.6f}, stand: {:0.6f}, raw: {:0.6f}", i.fitness.adjusted_fitness, i.fitness.standardized_fitness, i.fitness.raw_fitness);
|
||||||
|
i.tree.print(std::cout);
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_stats() const
|
||||||
|
{
|
||||||
|
// TODO: make stats helper
|
||||||
|
const auto& stats = program.get_population_stats();
|
||||||
|
BLT_INFO("Stats:");
|
||||||
|
BLT_INFO("Average fitness: {:0.6f}", stats.average_fitness.load());
|
||||||
|
BLT_INFO("Best fitness: {:0.6f}", stats.best_fitness.load());
|
||||||
|
BLT_INFO("Worst fitness: {:0.6f}", stats.worst_fitness.load());
|
||||||
|
BLT_INFO("Overall fitness: {:0.6f}", stats.overall_fitness.load());
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute()
|
||||||
|
{
|
||||||
|
setup_operations();
|
||||||
|
|
||||||
|
generate_initial_population();
|
||||||
|
|
||||||
|
run_generation_loop();
|
||||||
|
|
||||||
|
get_and_print_best();
|
||||||
|
|
||||||
|
print_stats();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const auto& get_training_cases() const
|
||||||
|
{
|
||||||
|
return training_cases;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::array<context, 200> training_cases{};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //BLT_GP_EXAMPLE_SYMBOLIC_REGRESSION_H
|
|
@ -0,0 +1,178 @@
|
||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLT_GP_ALLOCATOR_H
|
||||||
|
#define BLT_GP_ALLOCATOR_H
|
||||||
|
|
||||||
|
#include <blt/std/types.h>
|
||||||
|
#include <blt/logging/logging.h>
|
||||||
|
#include <blt/gp/util/trackers.h>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace blt::gp
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
#ifndef BLT_GP_MAX_ALIGNMENT
|
||||||
|
#define BLT_GP_MAX_ALIGNMENT 8
|
||||||
|
#endif
|
||||||
|
static constexpr inline size_t MAX_ALIGNMENT = BLT_GP_MAX_ALIGNMENT;
|
||||||
|
|
||||||
|
#if BLT_DEBUG_LEVEL > 0
|
||||||
|
static void check_alignment(const size_t bytes, const std::string& message = "Invalid alignment")
|
||||||
|
{
|
||||||
|
if (bytes % MAX_ALIGNMENT != 0)
|
||||||
|
{
|
||||||
|
BLT_ABORT((message + ", expected multiple of " + std::to_string(detail::MAX_ALIGNMENT) + " got "
|
||||||
|
+ std::to_string(bytes)).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BLT_TRACK_ALLOCATIONS
|
||||||
|
inline allocation_tracker_t tracker;
|
||||||
|
|
||||||
|
// population gen specifics
|
||||||
|
inline call_tracker_t crossover_calls;
|
||||||
|
inline call_tracker_t mutation_calls;
|
||||||
|
inline call_tracker_t reproduction_calls;
|
||||||
|
inline call_tracker_t crossover_allocations;
|
||||||
|
inline call_tracker_t mutation_allocations;
|
||||||
|
inline call_tracker_t reproduction_allocations;
|
||||||
|
|
||||||
|
// for evaluating fitness
|
||||||
|
inline call_tracker_t evaluation_calls;
|
||||||
|
inline call_tracker_t evaluation_allocations;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class aligned_allocator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void* allocate(blt::size_t bytes) // NOLINT
|
||||||
|
{
|
||||||
|
#ifdef BLT_TRACK_ALLOCATIONSS
|
||||||
|
tracker.allocate(bytes);
|
||||||
|
// std::cout << "Hey our aligned allocator allocated " << bytes << " bytes!\n";
|
||||||
|
#endif
|
||||||
|
#if (BLT_DEBUG_LEVEL > 0)
|
||||||
|
detail::check_alignment(bytes);
|
||||||
|
#endif
|
||||||
|
return std::aligned_alloc(detail::MAX_ALIGNMENT, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(void* ptr, blt::size_t bytes) // NOLINT
|
||||||
|
{
|
||||||
|
if (ptr == nullptr)
|
||||||
|
return;
|
||||||
|
#ifdef BLT_TRACK_ALLOCATIONS
|
||||||
|
tracker.deallocate(bytes);
|
||||||
|
// std::cout << "[Hey our aligned allocator deallocated " << bytes << " bytes!]\n";
|
||||||
|
#else
|
||||||
|
(void)bytes;
|
||||||
|
#endif
|
||||||
|
std::free(ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class tracked_allocator_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using reference = T&;
|
||||||
|
using const_reference = const T&;
|
||||||
|
using pointer = T*;
|
||||||
|
using const_pointer = const T*;
|
||||||
|
using void_pointer = void*;
|
||||||
|
using const_void_pointer = const void*;
|
||||||
|
using difference_type = blt::ptrdiff_t;
|
||||||
|
using size_type = blt::size_t;
|
||||||
|
|
||||||
|
template <class U>
|
||||||
|
struct rebind
|
||||||
|
{
|
||||||
|
typedef tracked_allocator_t<U> other;
|
||||||
|
};
|
||||||
|
|
||||||
|
pointer allocate(size_type n)
|
||||||
|
{
|
||||||
|
#ifdef BLT_TRACK_ALLOCATIONS
|
||||||
|
tracker.allocate(n * sizeof(T));
|
||||||
|
// std::cout << "Hey our tracked allocator allocated " << (n * sizeof(T)) << " bytes!\n";
|
||||||
|
#endif
|
||||||
|
return static_cast<pointer>(std::malloc(n * sizeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer allocate(size_type n, const_void_pointer)
|
||||||
|
{
|
||||||
|
return allocate(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(pointer p, size_type n)
|
||||||
|
{
|
||||||
|
#ifdef BLT_TRACK_ALLOCATIONS
|
||||||
|
::blt::gp::tracker.deallocate(n * sizeof(T));
|
||||||
|
// std::cout << "[Hey our tracked allocator deallocated " << (n * sizeof(T)) << " bytes!]\n";
|
||||||
|
#else
|
||||||
|
(void)n;
|
||||||
|
#endif
|
||||||
|
std::free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class U, class... Args>
|
||||||
|
void construct(U* p, Args&&... args)
|
||||||
|
{
|
||||||
|
new(p) T(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class U>
|
||||||
|
void destroy(U* p)
|
||||||
|
{
|
||||||
|
p->~T();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_type max_size() const noexcept
|
||||||
|
{
|
||||||
|
return std::numeric_limits<size_type>::max();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T1, class T2>
|
||||||
|
inline static bool operator==(const tracked_allocator_t<T1>& lhs, const tracked_allocator_t<T2>& rhs) noexcept
|
||||||
|
{
|
||||||
|
return &lhs == &rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T1, class T2>
|
||||||
|
inline static bool operator!=(const tracked_allocator_t<T1>& lhs, const tracked_allocator_t<T2>& rhs) noexcept
|
||||||
|
{
|
||||||
|
return &lhs != &rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BLT_TRACK_ALLOCATIONS
|
||||||
|
template<typename T>
|
||||||
|
using tracked_vector = std::vector<T, tracked_allocator_t<T>>;
|
||||||
|
#else
|
||||||
|
template <typename T>
|
||||||
|
using tracked_vector = std::vector<T>;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //BLT_GP_ALLOCATOR_H
|
|
@ -22,6 +22,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <blt/std/types.h>
|
#include <blt/std/types.h>
|
||||||
|
#include <blt/meta/config_generator.h>
|
||||||
#include <blt/gp/generators.h>
|
#include <blt/gp/generators.h>
|
||||||
#include <blt/gp/transformers.h>
|
#include <blt/gp/transformers.h>
|
||||||
|
|
||||||
|
@ -29,10 +30,11 @@ namespace blt::gp
|
||||||
{
|
{
|
||||||
struct prog_config_t
|
struct prog_config_t
|
||||||
{
|
{
|
||||||
blt::size_t population_size = 500;
|
size_t population_size = 500;
|
||||||
blt::size_t max_generations = 50;
|
size_t max_generations = 50;
|
||||||
blt::size_t initial_min_tree_size = 3;
|
size_t initial_min_tree_size = 2;
|
||||||
blt::size_t initial_max_tree_size = 10;
|
size_t initial_max_tree_size = 6;
|
||||||
|
size_t max_tree_depth = 17;
|
||||||
|
|
||||||
// percent chance that we will do crossover
|
// percent chance that we will do crossover
|
||||||
double crossover_chance = 0.8;
|
double crossover_chance = 0.8;
|
||||||
|
@ -42,7 +44,7 @@ namespace blt::gp
|
||||||
double reproduction_chance = 0.1;
|
double reproduction_chance = 0.1;
|
||||||
// everything else will just be selected
|
// everything else will just be selected
|
||||||
|
|
||||||
blt::size_t elites = 0;
|
size_t elites = 0;
|
||||||
|
|
||||||
bool try_mutation_on_crossover_failure = true;
|
bool try_mutation_on_crossover_failure = true;
|
||||||
|
|
||||||
|
@ -50,9 +52,9 @@ namespace blt::gp
|
||||||
std::reference_wrapper<crossover_t> crossover;
|
std::reference_wrapper<crossover_t> crossover;
|
||||||
std::reference_wrapper<population_initializer_t> pop_initializer;
|
std::reference_wrapper<population_initializer_t> pop_initializer;
|
||||||
|
|
||||||
blt::size_t threads = std::thread::hardware_concurrency();
|
size_t threads = std::thread::hardware_concurrency();
|
||||||
// number of elements each thread should pull per execution. this is for granularity performance and can be optimized for better results!
|
// number of elements each thread should pull per execution. this is for granularity performance and can be optimized for better results!
|
||||||
blt::size_t evaluation_size = 4;
|
size_t evaluation_size = 4;
|
||||||
|
|
||||||
// default config (ramped half-and-half init) or for buildering
|
// default config (ramped half-and-half init) or for buildering
|
||||||
prog_config_t();
|
prog_config_t();
|
||||||
|
@ -146,6 +148,12 @@ namespace blt::gp
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prog_config_t& set_max_tree_depth(const size_t depth)
|
||||||
|
{
|
||||||
|
max_tree_depth = depth;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
prog_config_t& set_reproduction_chance(double chance)
|
prog_config_t& set_reproduction_chance(double chance)
|
||||||
{
|
{
|
||||||
reproduction_chance = chance;
|
reproduction_chance = chance;
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLT_GP_DEFINES_H
|
||||||
|
#define BLT_GP_DEFINES_H
|
||||||
|
|
||||||
|
#include <blt/std/defines.h>
|
||||||
|
|
||||||
|
#if BLT_DEBUG_LEVEL > 0
|
||||||
|
#if defined(__has_include) &&__has_include(<opentelemetry/version.h>)
|
||||||
|
#define BLT_DEBUG_OTEL_ENABLED 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BLT_DEBUG_LEVEL > 1
|
||||||
|
#define BLT_GP_DEBUG_TRACK_ALLOCATIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BLT_DEBUG_LEVEL > 2
|
||||||
|
#define BLT_GP_DEBUG_CHECK_TREES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BLT_GP_DEBUG_TRACK_ALLOCATIONS
|
||||||
|
#undef BLT_GP_DEBUG_TRACK_ALLOCATIONS
|
||||||
|
#define BLT_GP_DEBUG_TRACK_ALLOCATIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BLT_GP_DEBUG_CHECK_TREES
|
||||||
|
#undef BLT_GP_DEBUG_CHECK_TREES
|
||||||
|
#define BLT_GP_DEBUG_CHECK_TREES 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //BLT_GP_DEFINES_H
|
|
@ -20,33 +20,14 @@
|
||||||
#define BLT_GP_FWDECL_H
|
#define BLT_GP_FWDECL_H
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <blt/std/logging.h>
|
#include <blt/logging/logging.h>
|
||||||
#include <blt/std/types.h>
|
#include <blt/std/types.h>
|
||||||
#include <blt/gp/stats.h>
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <cstdlib>
|
#include <blt/gp/util/trackers.h>
|
||||||
#include <mutex>
|
#include <blt/gp/allocator.h>
|
||||||
#include <atomic>
|
|
||||||
#include <blt/std/mmap.h>
|
|
||||||
|
|
||||||
namespace blt::gp
|
namespace blt::gp
|
||||||
{
|
{
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
inline allocation_tracker_t tracker;
|
|
||||||
|
|
||||||
// population gen specifics
|
|
||||||
inline call_tracker_t crossover_calls;
|
|
||||||
inline call_tracker_t mutation_calls;
|
|
||||||
inline call_tracker_t reproduction_calls;
|
|
||||||
inline call_tracker_t crossover_allocations;
|
|
||||||
inline call_tracker_t mutation_allocations;
|
|
||||||
inline call_tracker_t reproduction_allocations;
|
|
||||||
|
|
||||||
// for evaluating fitness
|
|
||||||
inline call_tracker_t evaluation_calls;
|
|
||||||
inline call_tracker_t evaluation_allocations;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class gp_program;
|
class gp_program;
|
||||||
|
|
||||||
class type;
|
class type;
|
||||||
|
@ -55,6 +36,8 @@ namespace blt::gp
|
||||||
|
|
||||||
struct type_id;
|
struct type_id;
|
||||||
|
|
||||||
|
struct operator_info_t;
|
||||||
|
|
||||||
class type_provider;
|
class type_provider;
|
||||||
|
|
||||||
struct op_container_t;
|
struct op_container_t;
|
||||||
|
@ -78,271 +61,6 @@ namespace blt::gp
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class tracked_allocator_t;
|
class tracked_allocator_t;
|
||||||
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
template<typename T>
|
|
||||||
using tracked_vector = std::vector<T, tracked_allocator_t<T>>;
|
|
||||||
#else
|
|
||||||
template<typename T>
|
|
||||||
using tracked_vector = std::vector<T>;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// using operation_vector_t = tracked_vector<op_container_t>;
|
|
||||||
// using individual_vector_t = tracked_vector<individual_t, tracked_allocator_t<individual_t>>;
|
|
||||||
// using tree_vector_t = tracked_vector<tree_t>;
|
|
||||||
|
|
||||||
class aligned_allocator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void* allocate(blt::size_t bytes) // NOLINT
|
|
||||||
{
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
tracker.allocate(bytes);
|
|
||||||
// std::cout << "Hey our aligned allocator allocated " << bytes << " bytes!\n";
|
|
||||||
#endif
|
|
||||||
return std::aligned_alloc(8, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate(void* ptr, blt::size_t bytes) // NOLINT
|
|
||||||
{
|
|
||||||
if (ptr == nullptr)
|
|
||||||
return;
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
tracker.deallocate(bytes);
|
|
||||||
// std::cout << "[Hey our aligned allocator deallocated " << bytes << " bytes!]\n";
|
|
||||||
#else
|
|
||||||
(void) bytes;
|
|
||||||
#endif
|
|
||||||
std::free(ptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename Alloc = blt::aligned_huge_allocator>
|
|
||||||
class variable_bump_allocator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit variable_bump_allocator(blt::size_t default_block_size = BLT_2MB_SIZE): default_block_size(default_block_size)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void* allocate(blt::size_t bytes)
|
|
||||||
{
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
tracker.allocate(bytes);
|
|
||||||
#endif
|
|
||||||
std::scoped_lock lock(mutex);
|
|
||||||
if (head == nullptr || head->remaining_bytes_in_block() < static_cast<blt::ptrdiff_t>(bytes))
|
|
||||||
{
|
|
||||||
push_block(bytes);
|
|
||||||
}
|
|
||||||
auto ptr = head->metadata.offset;
|
|
||||||
head->metadata.offset += bytes;
|
|
||||||
++head->metadata.allocated_objects;
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate(void* ptr, blt::size_t bytes)
|
|
||||||
{
|
|
||||||
if (ptr == nullptr)
|
|
||||||
return;
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
tracker.deallocate(bytes);
|
|
||||||
#else
|
|
||||||
(void) bytes;
|
|
||||||
#endif
|
|
||||||
std::scoped_lock lock(mutex);
|
|
||||||
block_t* blk = to_block(ptr);
|
|
||||||
--blk->metadata.allocated_objects;
|
|
||||||
if (blk->metadata.allocated_objects == 0)
|
|
||||||
{
|
|
||||||
if (blk->metadata.has_deallocated)
|
|
||||||
alloc.deallocate(blk, blk->metadata.size);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (head == blk)
|
|
||||||
head = head->metadata.next;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto prev = head;
|
|
||||||
auto next = head->metadata.next;
|
|
||||||
while (next != blk)
|
|
||||||
{
|
|
||||||
prev = next;
|
|
||||||
next = next->metadata.next;
|
|
||||||
}
|
|
||||||
prev->metadata.next = next->metadata.next;
|
|
||||||
}
|
|
||||||
deallocated_blocks.push_back(blk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~variable_bump_allocator()
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(mutex);
|
|
||||||
for (auto* blk : deallocated_blocks)
|
|
||||||
{
|
|
||||||
alloc.deallocate(blk, blk->metadata.size);
|
|
||||||
}
|
|
||||||
auto cur = head;
|
|
||||||
while (cur != nullptr)
|
|
||||||
{
|
|
||||||
auto* ptr = cur;
|
|
||||||
ptr->metadata.has_deallocated = true;
|
|
||||||
cur = cur->metadata.next;
|
|
||||||
}
|
|
||||||
head = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct block_t
|
|
||||||
{
|
|
||||||
struct block_metadata_t
|
|
||||||
{
|
|
||||||
blt::size_t size;
|
|
||||||
blt::size_t allocated_objects : 63;
|
|
||||||
bool has_deallocated : 1;
|
|
||||||
block_t* next;
|
|
||||||
blt::u8* offset;
|
|
||||||
} metadata;
|
|
||||||
blt::u8 buffer[8]{};
|
|
||||||
|
|
||||||
explicit block_t(blt::size_t size): metadata{size, 0, false, nullptr, nullptr}
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
metadata.offset = buffer;
|
|
||||||
metadata.allocated_objects = 0;
|
|
||||||
metadata.next = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] blt::ptrdiff_t storage_size() const noexcept
|
|
||||||
{
|
|
||||||
return static_cast<blt::ptrdiff_t>(metadata.size - sizeof(typename block_t::block_metadata_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] blt::ptrdiff_t used_bytes_in_block() const noexcept
|
|
||||||
{
|
|
||||||
return static_cast<blt::ptrdiff_t>(metadata.offset - buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] blt::ptrdiff_t remaining_bytes_in_block() const noexcept
|
|
||||||
{
|
|
||||||
return storage_size() - used_bytes_in_block();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline block_t* to_block(void* p)
|
|
||||||
{
|
|
||||||
return reinterpret_cast<block_t*>(reinterpret_cast<std::uintptr_t>(p) & static_cast<std::uintptr_t>(~(BLT_2MB_SIZE - 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void push_block(blt::size_t bytes)
|
|
||||||
{
|
|
||||||
auto blk = allocate_block(bytes);
|
|
||||||
// BLT_TRACE("Allocated block %p", blk);
|
|
||||||
blk->metadata.next = head;
|
|
||||||
head = blk;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline block_t* allocate_block(blt::size_t bytes)
|
|
||||||
{
|
|
||||||
if (!deallocated_blocks.empty())
|
|
||||||
{
|
|
||||||
block_t* blk = deallocated_blocks.back();
|
|
||||||
deallocated_blocks.pop_back();
|
|
||||||
blk->reset();
|
|
||||||
return blk;
|
|
||||||
}
|
|
||||||
auto size = align_size_to(bytes + sizeof(typename block_t::block_metadata_t), default_block_size);
|
|
||||||
auto* ptr = static_cast<block_t*>(alloc.allocate(size));
|
|
||||||
new(ptr) block_t{size};
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
block_t* head = nullptr;
|
|
||||||
std::mutex mutex;
|
|
||||||
std::vector<block_t*> deallocated_blocks;
|
|
||||||
blt::size_t default_block_size;
|
|
||||||
Alloc alloc;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class tracked_allocator_t
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using value_type = T;
|
|
||||||
using reference = T&;
|
|
||||||
using const_reference = const T&;
|
|
||||||
using pointer = T*;
|
|
||||||
using const_pointer = const T*;
|
|
||||||
using void_pointer = void*;
|
|
||||||
using const_void_pointer = const void*;
|
|
||||||
using difference_type = blt::ptrdiff_t;
|
|
||||||
using size_type = blt::size_t;
|
|
||||||
template<class U>
|
|
||||||
struct rebind
|
|
||||||
{
|
|
||||||
typedef tracked_allocator_t<U> other;
|
|
||||||
};
|
|
||||||
|
|
||||||
pointer allocate(size_type n)
|
|
||||||
{
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
tracker.allocate(n * sizeof(T));
|
|
||||||
// std::cout << "Hey our tracked allocator allocated " << (n * sizeof(T)) << " bytes!\n";
|
|
||||||
#endif
|
|
||||||
return static_cast<pointer>(std::malloc(n * sizeof(T)));
|
|
||||||
}
|
|
||||||
|
|
||||||
pointer allocate(size_type n, const_void_pointer)
|
|
||||||
{
|
|
||||||
return allocate(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate(pointer p, size_type n)
|
|
||||||
{
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
tracker.deallocate(n * sizeof(T));
|
|
||||||
// std::cout << "[Hey our tracked allocator deallocated " << (n * sizeof(T)) << " bytes!]\n";
|
|
||||||
#else
|
|
||||||
(void) n;
|
|
||||||
#endif
|
|
||||||
std::free(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class U, class... Args>
|
|
||||||
void construct(U* p, Args&& ... args)
|
|
||||||
{
|
|
||||||
new(p) T(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class U>
|
|
||||||
void destroy(U* p)
|
|
||||||
{
|
|
||||||
p->~T();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] size_type max_size() const noexcept
|
|
||||||
{
|
|
||||||
return std::numeric_limits<size_type>::max();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class T1, class T2>
|
|
||||||
inline static bool operator==(const tracked_allocator_t<T1>& lhs, const tracked_allocator_t<T2>& rhs) noexcept
|
|
||||||
{
|
|
||||||
return &lhs == &rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T1, class T2>
|
|
||||||
inline static bool operator!=(const tracked_allocator_t<T1>& lhs, const tracked_allocator_t<T2>& rhs) noexcept
|
|
||||||
{
|
|
||||||
return &lhs != &rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
class operator_storage_test;
|
class operator_storage_test;
|
||||||
|
@ -354,16 +72,28 @@ namespace blt::gp
|
||||||
|
|
||||||
enum class destroy_t
|
enum class destroy_t
|
||||||
{
|
{
|
||||||
ARGS,
|
PTR,
|
||||||
RETURN
|
RETURN
|
||||||
};
|
};
|
||||||
|
|
||||||
using destroy_func_t = std::function<void(destroy_t, stack_allocator&)>;
|
using destroy_func_t = std::function<void(destroy_t, u8*)>;
|
||||||
|
|
||||||
using const_op_iter_t = tracked_vector<op_container_t>::const_iterator;
|
using const_op_iter_t = tracked_vector<op_container_t>::const_iterator;
|
||||||
using op_iter_t = tracked_vector<op_container_t>::iterator;
|
using op_iter_t = tracked_vector<op_container_t>::iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if BLT_DEBUG_LEVEL > 0
|
||||||
|
|
||||||
|
namespace detail::debug
|
||||||
|
{
|
||||||
|
inline void* context_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BLT_GP_UPDATE_CONTEXT(context) blt::gp::detail::debug::context_ptr = const_cast<void*>(static_cast<const void*>(&context))
|
||||||
|
#else
|
||||||
|
#define BLT_GP_UPDATE_CONTEXT(context)
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //BLT_GP_FWDECL_H
|
#endif //BLT_GP_FWDECL_H
|
||||||
|
|
|
@ -22,77 +22,36 @@
|
||||||
#include <blt/std/types.h>
|
#include <blt/std/types.h>
|
||||||
#include <blt/gp/typesystem.h>
|
#include <blt/gp/typesystem.h>
|
||||||
#include <blt/gp/stack.h>
|
#include <blt/gp/stack.h>
|
||||||
|
#include <blt/gp/util/meta.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
namespace blt::gp
|
namespace blt::gp
|
||||||
{
|
{
|
||||||
namespace detail
|
template <typename Return, typename... Args>
|
||||||
{
|
|
||||||
template<typename T>
|
|
||||||
using remove_cv_ref = std::remove_cv_t<std::remove_reference_t<T>>;
|
|
||||||
|
|
||||||
|
|
||||||
template<typename...>
|
|
||||||
struct first_arg;
|
|
||||||
|
|
||||||
template<typename First, typename... Args>
|
|
||||||
struct first_arg<First, Args...>
|
|
||||||
{
|
|
||||||
using type = First;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct first_arg<>
|
|
||||||
{
|
|
||||||
using type = void;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<bool b, typename... types>
|
|
||||||
struct is_same;
|
|
||||||
|
|
||||||
template<typename... types>
|
|
||||||
struct is_same<true, types...> : public std::false_type
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename... types>
|
|
||||||
struct is_same<false, types...> : public std::is_same<types...>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename... types>
|
|
||||||
constexpr bool is_same_v = is_same<sizeof...(types) == 0, types...>::value;
|
|
||||||
|
|
||||||
struct empty_t
|
|
||||||
{
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Return, typename... Args>
|
|
||||||
struct call_with
|
struct call_with
|
||||||
{
|
{
|
||||||
template<blt::u64 index>
|
template <u64 index>
|
||||||
[[nodiscard]] inline constexpr static blt::size_t getByteOffset()
|
[[nodiscard]] constexpr static size_t getByteOffset()
|
||||||
{
|
{
|
||||||
blt::size_t offset = 0;
|
size_t offset = 0;
|
||||||
blt::size_t current_index = 0;
|
size_t current_index = 0;
|
||||||
((offset += (current_index++ > index ? stack_allocator::aligned_size<detail::remove_cv_ref<Args>>() : 0)), ...);
|
((offset += (current_index++ > index ? stack_allocator::aligned_size<detail::remove_cv_ref<Args>>() : 0)), ...);
|
||||||
// BLT_INFO("offset %ld for argument %ld", offset, index);
|
(void)current_index;
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<blt::u64... indices>
|
template <u64... indices>
|
||||||
void print_args(std::integer_sequence<blt::u64, indices...>)
|
void print_args(std::integer_sequence<u64, indices...>)
|
||||||
{
|
{
|
||||||
BLT_INFO("Arguments:");
|
BLT_INFO("Arguments:");
|
||||||
(BLT_INFO("%ld: %s", indices, blt::type_string<Args>().c_str()), ...);
|
(BLT_INFO("%ld: %s", indices, blt::type_string<Args>().c_str()), ...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Func, blt::u64... indices, typename... ExtraArgs>
|
template <typename Func, u64... indices, typename... ExtraArgs>
|
||||||
inline static constexpr Return exec_sequence_to_indices(Func&& func, stack_allocator& allocator, std::integer_sequence<blt::u64, indices...>,
|
static constexpr Return exec_sequence_to_indices(Func&& func, stack_allocator& allocator, std::integer_sequence<u64, indices...>,
|
||||||
ExtraArgs&& ... args)
|
ExtraArgs&&... args)
|
||||||
{
|
{
|
||||||
//blt::size_t arg_size = (stack_allocator::aligned_size<detail::remove_cv_ref<Args>>() + ...);
|
//blt::size_t arg_size = (stack_allocator::aligned_size<detail::remove_cv_ref<Args>>() + ...);
|
||||||
//BLT_TRACE(arg_size);
|
//BLT_TRACE(arg_size);
|
||||||
|
@ -101,28 +60,39 @@ namespace blt::gp
|
||||||
allocator.from<detail::remove_cv_ref<Args>>(getByteOffset<indices>())...);
|
allocator.from<detail::remove_cv_ref<Args>>(getByteOffset<indices>())...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename context = void, typename... NoCtxArgs>
|
template<typename T>
|
||||||
void call_destructors_without_first(stack_allocator& read_allocator)
|
static void call_drop(stack_allocator& read_allocator, const size_t offset)
|
||||||
{
|
{
|
||||||
if constexpr (sizeof...(NoCtxArgs) > 0)
|
if constexpr (blt::gp::detail::has_func_drop_v<detail::remove_cv_ref<T>>)
|
||||||
{
|
{
|
||||||
read_allocator.call_destructors<detail::remove_cv_ref<NoCtxArgs>...>();
|
auto [type, ptr] = read_allocator.access_pointer<detail::remove_cv_ref<T>>(offset);
|
||||||
|
// type is not ephemeral, so we must drop it.
|
||||||
|
if (!ptr.bit(0))
|
||||||
|
type.drop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Func, typename... ExtraArgs>
|
static void call_destructors(stack_allocator& read_allocator)
|
||||||
Return operator()(bool has_context, Func&& func, stack_allocator& read_allocator, ExtraArgs&& ... args)
|
|
||||||
{
|
{
|
||||||
constexpr auto seq = std::make_integer_sequence<blt::u64, sizeof...(Args)>();
|
if constexpr (sizeof...(Args) > 0)
|
||||||
|
{
|
||||||
|
size_t offset = (stack_allocator::aligned_size<detail::remove_cv_ref<Args>>() + ...) - stack_allocator::aligned_size<
|
||||||
|
detail::remove_cv_ref<typename meta::arg_helper<Args...>::First>>();
|
||||||
|
((call_drop<Args>(read_allocator, offset), offset -= stack_allocator::aligned_size<detail::remove_cv_ref<Args>>()), ...);
|
||||||
|
(void)offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Func, typename... ExtraArgs>
|
||||||
|
Return operator()(const bool, Func&& func, stack_allocator& read_allocator, ExtraArgs&&... args)
|
||||||
|
{
|
||||||
|
constexpr auto seq = std::make_integer_sequence<u64, sizeof...(Args)>();
|
||||||
#if BLT_DEBUG_LEVEL > 0
|
#if BLT_DEBUG_LEVEL > 0
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
Return ret = exec_sequence_to_indices(std::forward<Func>(func), read_allocator, seq, std::forward<ExtraArgs>(args)...);
|
Return ret = exec_sequence_to_indices(std::forward<Func>(func), read_allocator, seq, std::forward<ExtraArgs>(args)...);
|
||||||
if (has_context)
|
call_destructors(read_allocator);
|
||||||
call_destructors_without_first<Args...>(read_allocator);
|
|
||||||
else
|
|
||||||
read_allocator.call_destructors<detail::remove_cv_ref<Args>...>();
|
|
||||||
read_allocator.pop_bytes((stack_allocator::aligned_size<detail::remove_cv_ref<Args>>() + ...));
|
read_allocator.pop_bytes((stack_allocator::aligned_size<detail::remove_cv_ref<Args>>() + ...));
|
||||||
return ret;
|
return ret;
|
||||||
#if BLT_DEBUG_LEVEL > 0
|
#if BLT_DEBUG_LEVEL > 0
|
||||||
|
@ -135,120 +105,131 @@ namespace blt::gp
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Return, typename, typename... Args>
|
template <typename Return, typename, typename... Args>
|
||||||
struct call_without_first : public call_with<Return, Args...>
|
struct call_without_first : public call_with<Return, Args...>
|
||||||
{
|
{
|
||||||
using call_with<Return, Args...>::call_with;
|
using call_with<Return, Args...>::call_with;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename, typename>
|
template <typename, typename>
|
||||||
class operation_t;
|
class operation_t;
|
||||||
|
|
||||||
template<typename RawFunction, typename Return, typename... Args>
|
template <typename RawFunction, typename Return, typename... Args>
|
||||||
class operation_t<RawFunction, Return(Args...)>
|
class operation_t<RawFunction, Return(Args...)>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using function_t = RawFunction;
|
using function_t = RawFunction;
|
||||||
using First_Arg = typename blt::meta::arg_helper<Args...>::First;
|
using First_Arg = typename blt::meta::arg_helper<Args...>::First;
|
||||||
|
|
||||||
constexpr operation_t(const operation_t& copy) = default;
|
constexpr operation_t(const operation_t& copy) = default;
|
||||||
|
|
||||||
constexpr operation_t(operation_t&& move) = default;
|
constexpr operation_t(operation_t&& move) = default;
|
||||||
|
|
||||||
template<typename Functor>
|
template <typename Functor>
|
||||||
constexpr explicit operation_t(const Functor& functor, std::optional<std::string_view> name = {}): func(functor), name(name)
|
constexpr explicit operation_t(const Functor& functor, const std::optional<std::string_view> name = {}): func(functor), name(name)
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr inline Return operator()(stack_allocator& read_allocator) const
|
[[nodiscard]] constexpr Return operator()(stack_allocator& read_allocator) const
|
||||||
|
{
|
||||||
|
if constexpr (sizeof...(Args) == 0)
|
||||||
{
|
{
|
||||||
if constexpr (sizeof...(Args) == 0)
|
return func();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return call_with<Return, Args...>()(false, func, read_allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr Return operator()(void* context, stack_allocator& read_allocator) const
|
||||||
|
{
|
||||||
|
// should be an impossible state
|
||||||
|
if constexpr (sizeof...(Args) == 0)
|
||||||
|
{
|
||||||
|
BLT_ABORT("Cannot pass context to function without arguments!");
|
||||||
|
}
|
||||||
|
auto& ctx_ref = *static_cast<detail::remove_cv_ref<typename detail::first_arg<Args...>::type>*>(context);
|
||||||
|
if constexpr (sizeof...(Args) == 1)
|
||||||
|
{
|
||||||
|
return func(ctx_ref);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return call_without_first<Return, Args...>()(true, func, read_allocator, ctx_ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
[[nodiscard]] detail::operator_func_t make_callable() const
|
||||||
|
{
|
||||||
|
return [this](void* context, stack_allocator& read_allocator, stack_allocator& write_allocator)
|
||||||
|
{
|
||||||
|
if constexpr (detail::is_same_v<Context, detail::remove_cv_ref<typename detail::first_arg<Args...>::type>>)
|
||||||
{
|
{
|
||||||
return func();
|
// first arg is context
|
||||||
} else
|
write_allocator.push(this->operator()(context, read_allocator));
|
||||||
{
|
|
||||||
return call_with<Return, Args...>()(false, func, read_allocator);
|
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
|
||||||
[[nodiscard]] constexpr inline Return operator()(void* context, stack_allocator& read_allocator) const
|
|
||||||
{
|
|
||||||
// should be an impossible state
|
|
||||||
if constexpr (sizeof...(Args) == 0)
|
|
||||||
{
|
{
|
||||||
BLT_ABORT("Cannot pass context to function without arguments!");
|
// first arg isn't context
|
||||||
|
write_allocator.push(this->operator()(read_allocator));
|
||||||
}
|
}
|
||||||
auto& ctx_ref = *static_cast<detail::remove_cv_ref<typename detail::first_arg<Args...>::type>*>(context);
|
};
|
||||||
if constexpr (sizeof...(Args) == 1)
|
}
|
||||||
{
|
|
||||||
return func(ctx_ref);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
return call_without_first<Return, Args...>()(true, func, read_allocator, ctx_ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Context>
|
[[nodiscard]] inline constexpr std::optional<std::string_view> get_name() const
|
||||||
[[nodiscard]] detail::operator_func_t make_callable() const
|
{
|
||||||
{
|
return name;
|
||||||
return [this](void* context, stack_allocator& read_allocator, stack_allocator& write_allocator) {
|
}
|
||||||
if constexpr (detail::is_same_v<Context, detail::remove_cv_ref<typename detail::first_arg<Args...>::type>>)
|
|
||||||
{
|
|
||||||
// first arg is context
|
|
||||||
write_allocator.push(this->operator()(context, read_allocator));
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
// first arg isn't context
|
|
||||||
write_allocator.push(this->operator()(read_allocator));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] inline constexpr std::optional<std::string_view> get_name() const
|
inline constexpr const auto& get_function() const
|
||||||
{
|
{
|
||||||
return name;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline constexpr const auto& get_function() const
|
auto set_ephemeral()
|
||||||
{
|
{
|
||||||
return func;
|
is_ephemeral_ = true;
|
||||||
}
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
inline auto set_ephemeral()
|
[[nodiscard]] bool is_ephemeral() const
|
||||||
{
|
{
|
||||||
is_ephemeral_ = true;
|
return is_ephemeral_;
|
||||||
return *this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
inline bool is_ephemeral()
|
[[nodiscard]] bool return_has_ephemeral_drop() const
|
||||||
{
|
{
|
||||||
return is_ephemeral_;
|
return detail::has_func_drop_v<detail::remove_cv_ref<Return>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator_id id = -1;
|
operator_id id = -1;
|
||||||
private:
|
|
||||||
function_t func;
|
private:
|
||||||
std::optional<std::string_view> name;
|
function_t func;
|
||||||
bool is_ephemeral_ = false;
|
std::optional<std::string_view> name;
|
||||||
|
bool is_ephemeral_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename RawFunction, typename Return, typename Class, typename... Args>
|
template <typename RawFunction, typename Return, typename Class, typename... Args>
|
||||||
class operation_t<RawFunction, Return (Class::*)(Args...) const> : public operation_t<RawFunction, Return(Args...)>
|
class operation_t<RawFunction, Return (Class::*)(Args...) const> : public operation_t<RawFunction, Return(Args...)>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using operation_t<RawFunction, Return(Args...)>::operation_t;
|
using operation_t<RawFunction, Return(Args...)>::operation_t;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Lambda>
|
template <typename Lambda>
|
||||||
operation_t(Lambda) -> operation_t<Lambda, decltype(&Lambda::operator())>;
|
operation_t(Lambda) -> operation_t<Lambda, decltype(&Lambda::operator())>;
|
||||||
|
|
||||||
template<typename Return, typename... Args>
|
template <typename Return, typename... Args>
|
||||||
operation_t(Return(*)(Args...)) -> operation_t<Return(*)(Args...), Return(Args...)>;
|
operation_t(Return (*)(Args...)) -> operation_t<Return(*)(Args...), Return(Args...)>;
|
||||||
|
|
||||||
template<typename Lambda>
|
template <typename Lambda>
|
||||||
operation_t(Lambda, std::optional<std::string_view>) -> operation_t<Lambda, decltype(&Lambda::operator())>;
|
operation_t(Lambda, std::optional<std::string_view>) -> operation_t<Lambda, decltype(&Lambda::operator())>;
|
||||||
|
|
||||||
template<typename Return, typename... Args>
|
template <typename Return, typename... Args>
|
||||||
operation_t(Return(*)(Args...), std::optional<std::string_view>) -> operation_t<Return(*)(Args...), Return(Args...)>;
|
operation_t(Return (*)(Args...), std::optional<std::string_view>) -> operation_t<Return(*)(Args...), Return(Args...)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //BLT_GP_OPERATIONS_H
|
#endif //BLT_GP_OPERATIONS_H
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -20,15 +20,16 @@
|
||||||
#define BLT_GP_SELECTION_H
|
#define BLT_GP_SELECTION_H
|
||||||
|
|
||||||
#include <blt/gp/fwdecl.h>
|
#include <blt/gp/fwdecl.h>
|
||||||
|
#include <blt/gp/util/statistics.h>
|
||||||
#include <blt/gp/tree.h>
|
#include <blt/gp/tree.h>
|
||||||
#include <blt/gp/config.h>
|
#include <blt/gp/config.h>
|
||||||
#include <blt/gp/random.h>
|
#include <blt/gp/random.h>
|
||||||
#include <blt/std/assert.h>
|
#include <blt/std/assert.h>
|
||||||
#include "blt/std/format.h"
|
#include "blt/format/format.h"
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
namespace blt::gp
|
namespace blt::gp
|
||||||
{
|
{
|
||||||
|
|
||||||
struct selector_args
|
struct selector_args
|
||||||
{
|
{
|
||||||
gp_program& program;
|
gp_program& program;
|
||||||
|
@ -38,191 +39,122 @@ namespace blt::gp
|
||||||
random_t& random;
|
random_t& random;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr inline auto perform_elitism = [](const selector_args& args, population_t& next_pop) {
|
namespace detail
|
||||||
auto& [program, current_pop, current_stats, config, random] = args;
|
{
|
||||||
|
constexpr inline auto perform_elitism = [](const selector_args& args, population_t& next_pop)
|
||||||
if (config.elites > 0 && current_pop.get_individuals().size() >= config.elites)
|
|
||||||
{
|
{
|
||||||
static thread_local tracked_vector<std::pair<std::size_t, double>> values;
|
auto& [program, current_pop, current_stats, config, random] = args;
|
||||||
values.clear();
|
|
||||||
|
|
||||||
for (blt::size_t i = 0; i < config.elites; i++)
|
BLT_ASSERT_MSG(config.elites <= current_pop.get_individuals().size(), ("Not enough individuals in population (" +
|
||||||
values.emplace_back(i, current_pop.get_individuals()[i].fitness.adjusted_fitness);
|
std::to_string(current_pop.get_individuals().size()) +
|
||||||
|
") for requested amount of elites (" + std::to_string(config.elites) + ")").c_str());
|
||||||
|
|
||||||
for (const auto& ind : blt::enumerate(current_pop.get_individuals()))
|
if (config.elites > 0 && current_pop.get_individuals().size() >= config.elites)
|
||||||
{
|
{
|
||||||
for (blt::size_t i = 0; i < config.elites; i++)
|
thread_local tracked_vector<std::pair<std::size_t, double>> values;
|
||||||
|
values.clear();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < config.elites; i++)
|
||||||
|
values.emplace_back(i, current_pop.get_individuals()[i].fitness.adjusted_fitness);
|
||||||
|
|
||||||
|
for (const auto& ind : blt::enumerate(current_pop.get_individuals()))
|
||||||
{
|
{
|
||||||
if (ind.second.fitness.adjusted_fitness >= values[i].second)
|
for (size_t i = 0; i < config.elites; i++)
|
||||||
{
|
{
|
||||||
bool doesnt_contain = true;
|
if (ind.second.fitness.adjusted_fitness >= values[i].second)
|
||||||
for (blt::size_t j = 0; j < config.elites; j++)
|
|
||||||
{
|
{
|
||||||
if (ind.first == values[j].first)
|
bool doesnt_contain = true;
|
||||||
doesnt_contain = false;
|
for (blt::size_t j = 0; j < config.elites; j++)
|
||||||
|
{
|
||||||
|
if (ind.first == values[j].first)
|
||||||
|
doesnt_contain = false;
|
||||||
|
}
|
||||||
|
if (doesnt_contain)
|
||||||
|
values[i] = {ind.first, ind.second.fitness.adjusted_fitness};
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (doesnt_contain)
|
|
||||||
values[i] = {ind.first, ind.second.fitness.adjusted_fitness};
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < config.elites; i++)
|
||||||
|
next_pop.get_individuals()[i].copy_fast(current_pop.get_individuals()[values[i].first].tree);
|
||||||
|
return config.elites;
|
||||||
}
|
}
|
||||||
|
return 0ul;
|
||||||
|
};
|
||||||
|
|
||||||
for (blt::size_t i = 0; i < config.elites; i++)
|
}
|
||||||
next_pop.get_individuals()[i].copy_fast(current_pop.get_individuals()[values[i].first].tree);
|
|
||||||
return config.elites;
|
|
||||||
}
|
|
||||||
return 0ul;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename Crossover, typename Mutation, typename Reproduction>
|
|
||||||
constexpr inline auto default_next_pop_creator = [](
|
|
||||||
blt::gp::selector_args& args, Crossover& crossover_selection, Mutation& mutation_selection, Reproduction& reproduction_selection,
|
|
||||||
tree_t& c1, tree_t* c2) {
|
|
||||||
auto& [program, current_pop, current_stats, config, random] = args;
|
|
||||||
|
|
||||||
int sel = random.get_i32(0, 3);
|
|
||||||
switch (sel)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
if (c2 == nullptr)
|
|
||||||
return 0;
|
|
||||||
// everyone gets a chance once per loop.
|
|
||||||
if (random.choice(config.crossover_chance))
|
|
||||||
{
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
auto state = tracker.start_measurement_thread_local();
|
|
||||||
#endif
|
|
||||||
// crossover
|
|
||||||
const tree_t* p1;
|
|
||||||
const tree_t* p2;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
p1 = &crossover_selection.select(program, current_pop);
|
|
||||||
p2 = &crossover_selection.select(program, current_pop);
|
|
||||||
} while (!config.crossover.get().apply(program, *p1, *p2, c1, *c2));
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
tracker.stop_measurement_thread_local(state);
|
|
||||||
crossover_calls.call();
|
|
||||||
crossover_calls.set_value(std::max(crossover_calls.get_value(), state.getAllocatedByteDifference()));
|
|
||||||
if (state.getAllocatedByteDifference() != 0)
|
|
||||||
{
|
|
||||||
crossover_allocations.call(state.getAllocatedByteDifference());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (random.choice(config.mutation_chance))
|
|
||||||
{
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
auto state = tracker.start_measurement_thread_local();
|
|
||||||
#endif
|
|
||||||
// mutation
|
|
||||||
const tree_t* p;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
p = &mutation_selection.select(program, current_pop);
|
|
||||||
} while (!config.mutator.get().apply(program, *p, c1));
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
tracker.stop_measurement_thread_local(state);
|
|
||||||
mutation_calls.call();
|
|
||||||
mutation_calls.set_value(std::max(mutation_calls.get_value(), state.getAllocatedByteDifference()));
|
|
||||||
if (state.getAllocationDifference() != 0)
|
|
||||||
{
|
|
||||||
mutation_allocations.call(state.getAllocatedByteDifference());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
if (config.reproduction_chance > 0 && random.choice(config.reproduction_chance))
|
|
||||||
{
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
auto state = tracker.start_measurement_thread_local();
|
|
||||||
#endif
|
|
||||||
// reproduction
|
|
||||||
c1 = reproduction_selection.select(program, current_pop);
|
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
|
||||||
tracker.stop_measurement_thread_local(state);
|
|
||||||
reproduction_calls.call();
|
|
||||||
reproduction_calls.set_value(std::max(reproduction_calls.get_value(), state.getAllocatedByteDifference()));
|
|
||||||
if (state.getAllocationDifference() != 0)
|
|
||||||
{
|
|
||||||
reproduction_allocations.call(state.getAllocatedByteDifference());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
#if BLT_DEBUG_LEVEL > 0
|
|
||||||
BLT_ABORT("This is not possible!");
|
|
||||||
#else
|
|
||||||
BLT_UNREACHABLE;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class selection_t
|
class selection_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @param program gp program to select with, used in randoms
|
* @param program gp program to select with, used in randoms
|
||||||
* @param pop population to select from
|
* @param pop population to select from
|
||||||
* @param stats the populations statistics
|
* @param stats the populations statistics
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
virtual const tree_t& select(gp_program& program, const population_t& pop) = 0;
|
virtual const tree_t& select(gp_program& program, const population_t& pop) = 0;
|
||||||
|
|
||||||
virtual void pre_process(gp_program&, population_t&)
|
/**
|
||||||
{}
|
* Is run once on a single thread before selection begins. allows you to preprocess the generation for fitness metrics.
|
||||||
|
* TODO a method for parallel execution
|
||||||
|
*/
|
||||||
|
virtual void pre_process(gp_program&, population_t&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
virtual ~selection_t() = default;
|
virtual ~selection_t() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
class select_best_t : public selection_t
|
class select_best_t final : public selection_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const tree_t& select(gp_program& program, const population_t& pop) final;
|
void pre_process(gp_program&, population_t&) override;
|
||||||
|
|
||||||
|
const tree_t& select(gp_program& program, const population_t& pop) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic_uint64_t index = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class select_worst_t : public selection_t
|
class select_worst_t final : public selection_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const tree_t& select(gp_program& program, const population_t& pop) final;
|
void pre_process(gp_program&, population_t&) override;
|
||||||
|
|
||||||
|
const tree_t& select(gp_program& program, const population_t& pop) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic_uint64_t index = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class select_random_t : public selection_t
|
class select_random_t final : public selection_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const tree_t& select(gp_program& program, const population_t& pop) final;
|
const tree_t& select(gp_program& program, const population_t& pop) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class select_tournament_t : public selection_t
|
class select_tournament_t final : public selection_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit select_tournament_t(blt::size_t selection_size = 3): selection_size(selection_size)
|
explicit select_tournament_t(const size_t selection_size = 3): selection_size(selection_size)
|
||||||
{
|
{
|
||||||
if (selection_size == 0)
|
if (selection_size == 0)
|
||||||
BLT_ABORT("Unable to select with this size. Must select at least 1 individual_t!");
|
BLT_ABORT("Unable to select with this size. Must select at least 1 individual_t!");
|
||||||
}
|
}
|
||||||
|
|
||||||
const tree_t& select(gp_program& program, const population_t& pop) final;
|
const tree_t& select(gp_program& program, const population_t& pop) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const blt::size_t selection_size;
|
const size_t selection_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
class select_fitness_proportionate_t : public selection_t
|
class select_fitness_proportionate_t final : public selection_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const tree_t& select(gp_program& program, const population_t& pop) final;
|
const tree_t& select(gp_program& program, const population_t& pop) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //BLT_GP_SELECTION_H
|
#endif //BLT_GP_SELECTION_H
|
||||||
|
|
|
@ -21,20 +21,16 @@
|
||||||
|
|
||||||
#include <blt/std/types.h>
|
#include <blt/std/types.h>
|
||||||
#include <blt/std/bump_allocator.h>
|
#include <blt/std/bump_allocator.h>
|
||||||
#include <blt/std/assert.h>
|
#include <blt/logging/logging.h>
|
||||||
#include <blt/std/logging.h>
|
|
||||||
#include <blt/std/allocator.h>
|
#include <blt/std/allocator.h>
|
||||||
#include <blt/std/ranges.h>
|
#include <blt/meta/meta.h>
|
||||||
#include <blt/std/meta.h>
|
#include <blt/gp/util/meta.h>
|
||||||
#include <blt/gp/fwdecl.h>
|
#include <blt/gp/allocator.h>
|
||||||
#include <blt/gp/stats.h>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <stdexcept>
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace blt::gp
|
namespace blt::gp
|
||||||
{
|
{
|
||||||
|
@ -43,295 +39,341 @@ namespace blt::gp
|
||||||
BLT_META_MAKE_FUNCTION_CHECK(drop);
|
BLT_META_MAKE_FUNCTION_CHECK(drop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is the primary class that enables a type-erased GP system without compromising on performance.
|
||||||
|
*
|
||||||
|
* This class provides an efficient way to allocate, deallocate, and manage memory blocks
|
||||||
|
* in a stack-like structure. It supports operations like memory alignment, copying, moving,
|
||||||
|
* insertion, and removal of memory. This is particularly useful for performance-critical
|
||||||
|
* systems requiring temporary memory management without frequent heap allocation overhead.
|
||||||
|
*
|
||||||
|
* Types placed within this container cannot have an alignment greater than `BLT_GP_MAX_ALIGNMENT` bytes, doing so will result in unaligned pointer access.
|
||||||
|
* You can configure this by setting `BLT_GP_MAX_ALIGNMENT` as a compiler definition but be aware it will increase memory requirements.
|
||||||
|
* Setting `BLT_GP_MAX_ALIGNMENT` to lower than 8 is UB on x86-64 systems.
|
||||||
|
* Consequently, all types have a minimum storage size of `BLT_GP_MAX_ALIGNMENT` (8) bytes, meaning a char, float, int, etc. will take `BLT_GP_MAX_ALIGNMENT` bytes
|
||||||
|
*/
|
||||||
class stack_allocator
|
class stack_allocator
|
||||||
{
|
{
|
||||||
constexpr static blt::size_t PAGE_SIZE = 0x100;
|
constexpr static size_t PAGE_SIZE = 0x100;
|
||||||
constexpr static blt::size_t MAX_ALIGNMENT = 8;
|
using Allocator = aligned_allocator;
|
||||||
template<typename T>
|
|
||||||
using NO_REF_T = std::remove_cv_t<std::remove_reference_t<T>>;
|
|
||||||
using Allocator = aligned_allocator;
|
|
||||||
public:
|
|
||||||
static Allocator& get_allocator();
|
|
||||||
|
|
||||||
struct size_data_t
|
static constexpr size_t align_bytes(const size_t size) noexcept
|
||||||
|
{
|
||||||
|
return (size + (detail::MAX_ALIGNMENT - 1)) & ~(detail::MAX_ALIGNMENT - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static Allocator& get_allocator();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static constexpr size_t aligned_size() noexcept
|
||||||
|
{
|
||||||
|
const auto bytes = align_bytes(sizeof(std::decay_t<T>));
|
||||||
|
if constexpr (blt::gp::detail::has_func_drop_v<detail::remove_cv_ref<T>>)
|
||||||
|
return bytes + align_bytes(sizeof(std::atomic_uint64_t*));
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_allocator() = default;
|
||||||
|
|
||||||
|
stack_allocator(const stack_allocator& copy)
|
||||||
|
{
|
||||||
|
if (copy.data_ == nullptr || copy.bytes_stored == 0)
|
||||||
|
return;
|
||||||
|
expand(copy.size_);
|
||||||
|
std::memcpy(data_, copy.data_, copy.bytes_stored);
|
||||||
|
bytes_stored = copy.bytes_stored;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_allocator(stack_allocator&& move) noexcept:
|
||||||
|
data_(std::exchange(move.data_, nullptr)), bytes_stored(std::exchange(move.bytes_stored, 0)), size_(std::exchange(move.size_, 0))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_allocator& operator=(const stack_allocator& copy) = delete;
|
||||||
|
|
||||||
|
stack_allocator& operator=(stack_allocator&& move) noexcept
|
||||||
|
{
|
||||||
|
data_ = std::exchange(move.data_, data_);
|
||||||
|
size_ = std::exchange(move.size_, size_);
|
||||||
|
bytes_stored = std::exchange(move.bytes_stored, bytes_stored);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~stack_allocator()
|
||||||
|
{
|
||||||
|
get_allocator().deallocate(data_, size_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(const stack_allocator& stack)
|
||||||
|
{
|
||||||
|
if (stack.empty())
|
||||||
|
return;
|
||||||
|
if (stack.bytes_stored + bytes_stored > size_)
|
||||||
|
expand(stack.bytes_stored + size_);
|
||||||
|
std::memcpy(data_ + bytes_stored, stack.data_, stack.bytes_stored);
|
||||||
|
bytes_stored += stack.bytes_stored;
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_from(const stack_allocator& stack, const size_t bytes)
|
||||||
|
{
|
||||||
|
// TODO: add debug checks to these functions! (check for out of bounds copy)
|
||||||
|
if (bytes == 0)
|
||||||
|
return;
|
||||||
|
if (bytes + bytes_stored > size_)
|
||||||
|
expand(bytes + size_);
|
||||||
|
std::memcpy(data_ + bytes_stored, stack.data_ + (stack.bytes_stored - bytes), bytes);
|
||||||
|
bytes_stored += bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_from(const stack_allocator& stack, const size_t bytes, const size_t offset)
|
||||||
|
{
|
||||||
|
if (bytes == 0)
|
||||||
|
return;
|
||||||
|
if (bytes + bytes_stored > size_)
|
||||||
|
expand(bytes + size_);
|
||||||
|
std::memcpy(data_ + bytes_stored, stack.data_ + (stack.bytes_stored - bytes - offset), bytes);
|
||||||
|
bytes_stored += bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_from(const u8* data, const size_t bytes)
|
||||||
|
{
|
||||||
|
if (bytes == 0 || data == nullptr)
|
||||||
|
return;
|
||||||
|
if (bytes + bytes_stored > size_)
|
||||||
|
expand(bytes + size_);
|
||||||
|
std::memcpy(data_ + bytes_stored, data, bytes);
|
||||||
|
bytes_stored += bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_to(u8* data, const size_t bytes) const
|
||||||
|
{
|
||||||
|
if (bytes == 0 || data == nullptr)
|
||||||
|
return;
|
||||||
|
std::memcpy(data, data_ + (bytes_stored - bytes), bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void push(const T& t)
|
||||||
|
{
|
||||||
|
using DecayedT = std::decay_t<T>;
|
||||||
|
static_assert(std::is_trivially_copyable_v<DecayedT>, "Type must be bitwise copyable!");
|
||||||
|
static_assert(alignof(DecayedT) <= detail::MAX_ALIGNMENT, "Type alignment must not be greater than the max alignment!");
|
||||||
|
const auto ptr = static_cast<char*>(allocate_bytes_for_size(aligned_size<DecayedT>()));
|
||||||
|
std::memcpy(ptr, &t, sizeof(DecayedT));
|
||||||
|
|
||||||
|
if constexpr (gp::detail::has_func_drop_v<detail::remove_cv_ref<T>>)
|
||||||
{
|
{
|
||||||
blt::size_t total_size_bytes = 0;
|
new(ptr + sizeof(DecayedT)) mem::pointer_storage<std::atomic_uint64_t>{nullptr};
|
||||||
blt::size_t total_used_bytes = 0;
|
}
|
||||||
blt::size_t total_remaining_bytes = 0;
|
}
|
||||||
|
|
||||||
friend std::ostream& operator<<(std::ostream& stream, const size_data_t& data)
|
template <typename T>
|
||||||
{
|
T pop()
|
||||||
stream << "[";
|
{
|
||||||
stream << data.total_used_bytes << " / " << data.total_size_bytes;
|
using DecayedT = std::decay_t<T>;
|
||||||
stream << " ("
|
static_assert(std::is_trivially_copyable_v<DecayedT>, "Type must be bitwise copyable!");
|
||||||
<< (data.total_size_bytes != 0 ? (static_cast<double>(data.total_used_bytes) / static_cast<double>(data.total_size_bytes) *
|
static_assert(alignof(DecayedT) <= detail::MAX_ALIGNMENT, "Type alignment must not be greater than the max alignment!");
|
||||||
100) : 0) << "%); space left: " << data.total_remaining_bytes << "]";
|
constexpr auto size = aligned_size<DecayedT>();
|
||||||
return stream;
|
#if BLT_DEBUG_LEVEL > 0
|
||||||
}
|
if (bytes_stored < size)
|
||||||
|
throw std::runtime_error(("Not enough bytes left to pop!" __FILE__ ":") + std::to_string(__LINE__));
|
||||||
|
#endif
|
||||||
|
bytes_stored -= size;
|
||||||
|
return *reinterpret_cast<T*>(data_ + bytes_stored);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u8* from(const size_t bytes) const
|
||||||
|
{
|
||||||
|
#if BLT_DEBUG_LEVEL > 0
|
||||||
|
if (bytes_stored < bytes)
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Not enough bytes in stack! " + std::to_string(bytes) + " bytes requested but only " + std::to_string(bytes_stored) +
|
||||||
|
(" bytes stored! (at " __FILE__ ":") + std::to_string(__LINE__));
|
||||||
|
#endif
|
||||||
|
return data_ + (bytes_stored - bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T& from(const size_t bytes) const
|
||||||
|
{
|
||||||
|
using DecayedT = std::decay_t<T>;
|
||||||
|
static_assert(std::is_trivially_copyable_v<DecayedT> && "Type must be bitwise copyable!");
|
||||||
|
static_assert(alignof(DecayedT) <= detail::MAX_ALIGNMENT && "Type alignment must not be greater than the max alignment!");
|
||||||
|
return *reinterpret_cast<DecayedT*>(from(aligned_size<DecayedT>() + bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::pair<u8*, mem::pointer_storage<std::atomic_uint64_t>&> access_pointer(const size_t bytes, const size_t type_size) const
|
||||||
|
{
|
||||||
|
const auto type_ref = from(bytes);
|
||||||
|
return {
|
||||||
|
type_ref, *std::launder(
|
||||||
|
reinterpret_cast<mem::pointer_storage<std::atomic_uint64_t>*>(type_ref + (type_size - align_bytes(
|
||||||
|
sizeof(std::atomic_uint64_t*)))))
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
[[nodiscard]] std::pair<u8*, mem::pointer_storage<std::atomic_uint64_t>&> access_pointer_forward(
|
||||||
static inline constexpr blt::size_t aligned_size() noexcept
|
const size_t bytes, const size_t type_size) const
|
||||||
{
|
{
|
||||||
return aligned_size(sizeof(NO_REF_T<T>));
|
const auto type_ref = data_ + bytes;
|
||||||
}
|
return {
|
||||||
|
type_ref, *std::launder(
|
||||||
|
reinterpret_cast<mem::pointer_storage<std::atomic_uint64_t>*>(type_ref + (type_size - align_bytes(
|
||||||
|
sizeof(std::atomic_uint64_t*)))))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static inline constexpr blt::size_t aligned_size(blt::size_t size) noexcept
|
template <typename T>
|
||||||
{
|
[[nodiscard]] std::pair<T&, mem::pointer_storage<std::atomic_uint64_t>&> access_pointer(const size_t bytes) const
|
||||||
return (size + (MAX_ALIGNMENT - 1)) & ~(MAX_ALIGNMENT - 1);
|
{
|
||||||
}
|
auto& type_ref = from<T>(bytes);
|
||||||
|
return {
|
||||||
|
type_ref, *std::launder(
|
||||||
|
reinterpret_cast<mem::pointer_storage<std::atomic_uint64_t>*>(reinterpret_cast<char*>(&type_ref) +
|
||||||
|
align_bytes(sizeof(T))))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
stack_allocator() = default;
|
void pop_bytes(const size_t bytes)
|
||||||
|
{
|
||||||
stack_allocator(const stack_allocator& copy)
|
|
||||||
{
|
|
||||||
if (copy.data_ == nullptr || copy.bytes_stored == 0)
|
|
||||||
return;
|
|
||||||
expand(copy.size_);
|
|
||||||
std::memcpy(data_, copy.data_, copy.bytes_stored);
|
|
||||||
bytes_stored = copy.bytes_stored;
|
|
||||||
}
|
|
||||||
|
|
||||||
stack_allocator(stack_allocator&& move) noexcept:
|
|
||||||
data_(std::exchange(move.data_, nullptr)), bytes_stored(std::exchange(move.bytes_stored, 0)), size_(std::exchange(move.size_, 0))
|
|
||||||
{}
|
|
||||||
|
|
||||||
stack_allocator& operator=(const stack_allocator& copy) = delete;
|
|
||||||
|
|
||||||
stack_allocator& operator=(stack_allocator&& move) noexcept
|
|
||||||
{
|
|
||||||
data_ = std::exchange(move.data_, data_);
|
|
||||||
size_ = std::exchange(move.size_, size_);
|
|
||||||
bytes_stored = std::exchange(move.bytes_stored, bytes_stored);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~stack_allocator()
|
|
||||||
{
|
|
||||||
get_allocator().deallocate(data_, size_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void insert(const stack_allocator& stack)
|
|
||||||
{
|
|
||||||
if (stack.empty())
|
|
||||||
return;
|
|
||||||
if (stack.bytes_stored + bytes_stored > size_)
|
|
||||||
expand(stack.bytes_stored + size_);
|
|
||||||
std::memcpy(data_ + bytes_stored, stack.data_, stack.bytes_stored);
|
|
||||||
bytes_stored += stack.bytes_stored;
|
|
||||||
}
|
|
||||||
|
|
||||||
void copy_from(const stack_allocator& stack, blt::size_t bytes)
|
|
||||||
{
|
|
||||||
if (bytes == 0)
|
|
||||||
return;
|
|
||||||
if (bytes + bytes_stored > size_)
|
|
||||||
expand(bytes + size_);
|
|
||||||
std::memcpy(data_ + bytes_stored, stack.data_ + (stack.bytes_stored - bytes), bytes);
|
|
||||||
bytes_stored += bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void copy_from(blt::u8* data, blt::size_t bytes)
|
|
||||||
{
|
|
||||||
if (bytes == 0 || data == nullptr)
|
|
||||||
return;
|
|
||||||
if (bytes + bytes_stored > size_)
|
|
||||||
expand(bytes + size_);
|
|
||||||
std::memcpy(data_ + bytes_stored, data, bytes);
|
|
||||||
bytes_stored += bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void copy_to(blt::u8* data, blt::size_t bytes)
|
|
||||||
{
|
|
||||||
if (bytes == 0 || data == nullptr)
|
|
||||||
return;
|
|
||||||
std::memcpy(data, data_ + (bytes_stored - bytes), bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename NO_REF = NO_REF_T<T>>
|
|
||||||
void push(const T& t)
|
|
||||||
{
|
|
||||||
static_assert(std::is_trivially_copyable_v<NO_REF> && "Type must be bitwise copyable!");
|
|
||||||
static_assert(alignof(NO_REF) <= MAX_ALIGNMENT && "Type alignment must not be greater than the max alignment!");
|
|
||||||
auto ptr = allocate_bytes_for_size(sizeof(NO_REF));
|
|
||||||
std::memcpy(ptr, &t, sizeof(NO_REF));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename NO_REF = NO_REF_T<T>>
|
|
||||||
T pop()
|
|
||||||
{
|
|
||||||
static_assert(std::is_trivially_copyable_v<NO_REF> && "Type must be bitwise copyable!");
|
|
||||||
static_assert(alignof(NO_REF) <= MAX_ALIGNMENT && "Type alignment must not be greater than the max alignment!");
|
|
||||||
constexpr auto size = aligned_size(sizeof(NO_REF));
|
|
||||||
#if BLT_DEBUG_LEVEL > 0
|
#if BLT_DEBUG_LEVEL > 0
|
||||||
if (bytes_stored < size)
|
if (bytes_stored < bytes)
|
||||||
BLT_ABORT("Not enough bytes left to pop!");
|
BLT_ABORT(("Not enough bytes in stack to pop " + std::to_string(bytes) + " bytes requested but " + std::to_string(bytes) +
|
||||||
|
" bytes stored!").c_str());
|
||||||
|
gp::detail::check_alignment(bytes);
|
||||||
#endif
|
#endif
|
||||||
bytes_stored -= size;
|
bytes_stored -= bytes;
|
||||||
return *reinterpret_cast<T*>(data_ + bytes_stored);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] blt::u8* from(blt::size_t bytes) const
|
void transfer_bytes(stack_allocator& to, const size_t aligned_bytes)
|
||||||
{
|
{
|
||||||
#if BLT_DEBUG_LEVEL > 0
|
#if BLT_DEBUG_LEVEL > 0
|
||||||
if (bytes_stored < bytes)
|
if (bytes_stored < aligned_bytes)
|
||||||
BLT_ABORT(("Not enough bytes in stack to reference " + std::to_string(bytes) + " bytes requested but " + std::to_string(bytes) +
|
BLT_ABORT(
|
||||||
" bytes stored!").c_str());
|
("Not enough bytes in stack to transfer " + std::to_string(aligned_bytes) + " bytes requested but " + std::to_string(aligned_bytes) +
|
||||||
|
" bytes stored!").c_str());
|
||||||
|
gp::detail::check_alignment(aligned_bytes);
|
||||||
#endif
|
#endif
|
||||||
return data_ + (bytes_stored - bytes);
|
to.copy_from(*this, aligned_bytes);
|
||||||
}
|
pop_bytes(aligned_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T, typename NO_REF = NO_REF_T<T>>
|
[[nodiscard]] bool empty() const noexcept
|
||||||
T& from(blt::size_t bytes)
|
{
|
||||||
{
|
return bytes_stored == 0;
|
||||||
static_assert(std::is_trivially_copyable_v<NO_REF> && "Type must be bitwise copyable!");
|
}
|
||||||
static_assert(alignof(NO_REF) <= MAX_ALIGNMENT && "Type alignment must not be greater than the max alignment!");
|
|
||||||
return *reinterpret_cast<NO_REF*>(from(aligned_size(sizeof(NO_REF)) + bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
void pop_bytes(blt::size_t bytes)
|
[[nodiscard]] ptrdiff_t remainder() const noexcept
|
||||||
{
|
{
|
||||||
#if BLT_DEBUG_LEVEL > 0
|
return static_cast<ptrdiff_t>(size_ - bytes_stored);
|
||||||
if (bytes_stored < bytes)
|
}
|
||||||
BLT_ABORT(("Not enough bytes in stack to pop " + std::to_string(bytes) + " bytes requested but " + std::to_string(bytes) +
|
|
||||||
" bytes stored!").c_str());
|
|
||||||
#endif
|
|
||||||
bytes_stored -= bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void transfer_bytes(stack_allocator& to, blt::size_t bytes)
|
[[nodiscard]] size_t stored() const noexcept
|
||||||
{
|
{
|
||||||
#if BLT_DEBUG_LEVEL > 0
|
return bytes_stored;
|
||||||
if (bytes_stored < bytes)
|
}
|
||||||
BLT_ABORT(("Not enough bytes in stack to transfer " + std::to_string(bytes) + " bytes requested but " + std::to_string(bytes) +
|
|
||||||
" bytes stored!").c_str());
|
|
||||||
#endif
|
|
||||||
auto alg = aligned_size(bytes);
|
|
||||||
to.copy_from(*this, alg);
|
|
||||||
pop_bytes(alg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
void reserve(const size_t bytes)
|
||||||
void call_destructors()
|
{
|
||||||
{
|
if (bytes > size_)
|
||||||
if constexpr (sizeof...(Args) > 0)
|
|
||||||
{
|
|
||||||
blt::size_t offset = (stack_allocator::aligned_size(sizeof(NO_REF_T<Args>)) + ...) -
|
|
||||||
stack_allocator::aligned_size(sizeof(NO_REF_T<typename blt::meta::arg_helper<Args...>::First>));
|
|
||||||
((call_drop<Args>(offset), offset -= stack_allocator::aligned_size(sizeof(NO_REF_T<Args>))), ...);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool empty() const noexcept
|
|
||||||
{
|
|
||||||
return bytes_stored == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] blt::ptrdiff_t remaining_bytes_in_block() const noexcept
|
|
||||||
{
|
|
||||||
return static_cast<blt::ptrdiff_t>(size_ - bytes_stored);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] blt::ptrdiff_t bytes_in_head() const noexcept
|
|
||||||
{
|
|
||||||
return static_cast<blt::ptrdiff_t>(bytes_stored);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] size_data_t size() const noexcept
|
|
||||||
{
|
|
||||||
size_data_t data;
|
|
||||||
|
|
||||||
data.total_used_bytes = bytes_stored;
|
|
||||||
data.total_size_bytes = size_;
|
|
||||||
data.total_remaining_bytes = remaining_bytes_in_block();
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reserve(blt::size_t bytes)
|
|
||||||
{
|
|
||||||
if (bytes > size_)
|
|
||||||
expand_raw(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] blt::size_t stored() const
|
|
||||||
{
|
|
||||||
return bytes_stored;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] blt::size_t internal_storage_size() const
|
|
||||||
{
|
|
||||||
return size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
bytes_stored = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void expand(blt::size_t bytes)
|
|
||||||
{
|
|
||||||
//bytes = to_nearest_page_size(bytes);
|
|
||||||
expand_raw(bytes);
|
expand_raw(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void expand_raw(blt::size_t bytes)
|
void resize(const size_t bytes)
|
||||||
|
{
|
||||||
|
reserve(bytes);
|
||||||
|
bytes_stored = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t capacity() const
|
||||||
|
{
|
||||||
|
return size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
bytes_stored = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto* data() const
|
||||||
|
{
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void expand(const size_t bytes)
|
||||||
|
{
|
||||||
|
//bytes = to_nearest_page_size(bytes);
|
||||||
|
expand_raw(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void expand_raw(const size_t bytes)
|
||||||
|
{
|
||||||
|
// auto aligned = detail::aligned_size(bytes);
|
||||||
|
const auto new_data = static_cast<u8*>(get_allocator().allocate(bytes));
|
||||||
|
if (bytes_stored > 0)
|
||||||
|
std::memcpy(new_data, data_, bytes_stored);
|
||||||
|
get_allocator().deallocate(data_, size_);
|
||||||
|
data_ = new_data;
|
||||||
|
size_ = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t to_nearest_page_size(const size_t bytes) noexcept
|
||||||
|
{
|
||||||
|
constexpr static size_t MASK = ~(PAGE_SIZE - 1);
|
||||||
|
return (bytes & MASK) + PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] void* get_aligned_pointer(const size_t bytes) const noexcept
|
||||||
|
{
|
||||||
|
if (data_ == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
size_t remaining_bytes = remainder();
|
||||||
|
auto* pointer = static_cast<void*>(data_ + bytes_stored);
|
||||||
|
return std::align(gp::detail::MAX_ALIGNMENT, bytes, pointer, remaining_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* allocate_bytes_for_size(const size_t aligned_bytes)
|
||||||
|
{
|
||||||
|
#if BLT_DEBUG_LEVEL > 0
|
||||||
|
gp::detail::check_alignment(aligned_bytes);
|
||||||
|
#endif
|
||||||
|
auto aligned_ptr = get_aligned_pointer(aligned_bytes);
|
||||||
|
if (aligned_ptr == nullptr)
|
||||||
{
|
{
|
||||||
auto new_data = static_cast<blt::u8*>(get_allocator().allocate(bytes));
|
expand(size_ + aligned_bytes);
|
||||||
if (bytes_stored > 0)
|
aligned_ptr = get_aligned_pointer(aligned_bytes);
|
||||||
std::memcpy(new_data, data_, bytes_stored);
|
|
||||||
get_allocator().deallocate(data_, size_);
|
|
||||||
data_ = new_data;
|
|
||||||
size_ = bytes;
|
|
||||||
}
|
}
|
||||||
|
if (aligned_ptr == nullptr)
|
||||||
|
throw std::bad_alloc();
|
||||||
|
bytes_stored += aligned_bytes;
|
||||||
|
return aligned_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
static size_t to_nearest_page_size(blt::size_t bytes) noexcept
|
u8* data_ = nullptr;
|
||||||
{
|
// place in the data_ array which has a free spot.
|
||||||
constexpr static blt::size_t MASK = ~(PAGE_SIZE - 1);
|
size_t bytes_stored = 0;
|
||||||
return (bytes & MASK) + PAGE_SIZE;
|
size_t size_ = 0;
|
||||||
}
|
|
||||||
|
|
||||||
void* get_aligned_pointer(blt::size_t bytes) noexcept
|
|
||||||
{
|
|
||||||
if (data_ == nullptr)
|
|
||||||
return nullptr;
|
|
||||||
blt::size_t remaining_bytes = remaining_bytes_in_block();
|
|
||||||
auto* pointer = static_cast<void*>(data_ + bytes_stored);
|
|
||||||
return std::align(MAX_ALIGNMENT, bytes, pointer, remaining_bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* allocate_bytes_for_size(blt::size_t bytes)
|
|
||||||
{
|
|
||||||
auto used_bytes = aligned_size(bytes);
|
|
||||||
auto aligned_ptr = get_aligned_pointer(used_bytes);
|
|
||||||
if (aligned_ptr == nullptr)
|
|
||||||
{
|
|
||||||
expand(size_ + used_bytes);
|
|
||||||
aligned_ptr = get_aligned_pointer(used_bytes);
|
|
||||||
}
|
|
||||||
if (aligned_ptr == nullptr)
|
|
||||||
throw std::bad_alloc();
|
|
||||||
bytes_stored += used_bytes;
|
|
||||||
return aligned_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void call_drop(blt::size_t offset)
|
|
||||||
{
|
|
||||||
if constexpr (detail::has_func_drop_v<T>)
|
|
||||||
{
|
|
||||||
from<NO_REF_T<T >>(offset).drop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blt::u8* data_ = nullptr;
|
|
||||||
// place in the data_ array which has a free spot.
|
|
||||||
blt::size_t bytes_stored = 0;
|
|
||||||
blt::size_t size_ = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <size_t Size>
|
||||||
|
struct ref_counted_type
|
||||||
|
{
|
||||||
|
explicit ref_counted_type(size_t* ref_count): ref_count(ref_count)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t* ref_count = nullptr;
|
||||||
|
u8 storage[Size]{};
|
||||||
|
|
||||||
|
static size_t* access(const void* ptr)
|
||||||
|
{
|
||||||
|
ref_counted_type<1> type{nullptr};
|
||||||
|
std::memcpy(&type, ptr, sizeof(size_t*));
|
||||||
|
return type.ref_count;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //BLT_GP_STACK_H
|
#endif //BLT_GP_STACK_H
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLT_GP_SYNC_H
|
||||||
|
#define BLT_GP_SYNC_H
|
||||||
|
|
||||||
|
#include <blt/std/types.h>
|
||||||
|
#include <blt/gp/fwdecl.h>
|
||||||
|
|
||||||
|
namespace blt::gp
|
||||||
|
{
|
||||||
|
class sync_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit sync_t(gp_program& program, fs::writer_t& writer);
|
||||||
|
|
||||||
|
virtual void trigger(u64 current_time) const;
|
||||||
|
|
||||||
|
sync_t& with_timer(u64 seconds)
|
||||||
|
{
|
||||||
|
m_timer_seconds = seconds;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_t& every_generations(u64 generations)
|
||||||
|
{
|
||||||
|
m_generations = generations;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_t& overwrite_file_on_write()
|
||||||
|
{
|
||||||
|
m_reset_to_start_of_file = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_t& append_to_file_on_write()
|
||||||
|
{
|
||||||
|
m_reset_to_start_of_file = false;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the state of the whole program instead of just the generation information.
|
||||||
|
*/
|
||||||
|
sync_t& whole_program()
|
||||||
|
{
|
||||||
|
m_whole_program = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only save the current generation to disk.
|
||||||
|
*/
|
||||||
|
sync_t& generation_only()
|
||||||
|
{
|
||||||
|
m_whole_program = false;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~sync_t();
|
||||||
|
|
||||||
|
private:
|
||||||
|
gp_program* m_program;
|
||||||
|
fs::writer_t* m_writer;
|
||||||
|
std::optional<u64> m_timer_seconds;
|
||||||
|
std::optional<u64> m_generations;
|
||||||
|
bool m_reset_to_start_of_file = false;
|
||||||
|
bool m_whole_program = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //BLT_GP_SYNC_H
|
|
@ -0,0 +1,241 @@
|
||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLT_GP_THREADING_H
|
||||||
|
#define BLT_GP_THREADING_H
|
||||||
|
|
||||||
|
#include <blt/std/types.h>
|
||||||
|
#include <blt/std/thread.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <functional>
|
||||||
|
#include <atomic>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace blt::gp
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
struct empty_callable
|
||||||
|
{
|
||||||
|
void operator()() const
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename EnumId>
|
||||||
|
class task_builder_t;
|
||||||
|
|
||||||
|
template <typename EnumId, typename Parallel, typename Single = detail::empty_callable>
|
||||||
|
class task_t
|
||||||
|
{
|
||||||
|
static_assert(std::is_enum_v<EnumId>, "Enum ID must be of enum type!");
|
||||||
|
static_assert(std::is_invocable_v<Parallel, int>, "Parallel must be invocable with exactly one argument (thread index)");
|
||||||
|
static_assert(std::is_invocable_v<Single>, "Single must be invocable with no arguments");
|
||||||
|
|
||||||
|
friend task_builder_t<EnumId>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
task_t(const EnumId task_id, const Parallel& parallel, const Single& single): parallel(std::forward<Parallel>(parallel)),
|
||||||
|
single(std::forward<Single>(single)),
|
||||||
|
requires_single_sync(true), task_id(task_id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit task_t(const EnumId task_id, const Parallel& parallel): parallel(std::forward<Parallel>(parallel)), single(detail::empty_callable{}),
|
||||||
|
task_id(task_id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_parallel(size_t thread_index) const
|
||||||
|
{
|
||||||
|
parallel(thread_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_single() const
|
||||||
|
{
|
||||||
|
single();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] EnumId get_task_id() const
|
||||||
|
{
|
||||||
|
return task_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Parallel& parallel;
|
||||||
|
const Single& single;
|
||||||
|
bool requires_single_sync = false;
|
||||||
|
EnumId task_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename EnumId, typename Parallel, typename Single = detail::empty_callable>
|
||||||
|
task_t(EnumId, Parallel, Single) -> task_t<EnumId, Parallel, Single>;
|
||||||
|
|
||||||
|
template <typename EnumId>
|
||||||
|
class task_builder_t
|
||||||
|
{
|
||||||
|
static_assert(std::is_enum_v<EnumId>, "Enum ID must be of enum type!");
|
||||||
|
using EnumInt = std::underlying_type_t<EnumId>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
task_builder_t() = default;
|
||||||
|
|
||||||
|
template <typename... Tasks>
|
||||||
|
static std::function<void(barrier_t&, EnumId, size_t)> make_callable(Tasks&&... tasks)
|
||||||
|
{
|
||||||
|
return [&tasks...](barrier_t& sync_barrier, EnumId task, size_t thread_index)
|
||||||
|
{
|
||||||
|
call_jmp_table(sync_barrier, task, thread_index, tasks...);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename Task>
|
||||||
|
static void execute(barrier_t& sync_barrier, const size_t thread_index, Task&& task)
|
||||||
|
{
|
||||||
|
// sync_barrier.wait();
|
||||||
|
if (task.requires_single_sync)
|
||||||
|
{
|
||||||
|
if (thread_index == 0)
|
||||||
|
task.call_single();
|
||||||
|
sync_barrier.wait();
|
||||||
|
}
|
||||||
|
task.call_parallel(thread_index);
|
||||||
|
// sync_barrier.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Task>
|
||||||
|
static bool call(barrier_t& sync_barrier, const EnumId current_task, const size_t thread_index, Task&& task)
|
||||||
|
{
|
||||||
|
if (static_cast<EnumInt>(current_task) == static_cast<EnumInt>(task.get_task_id()))
|
||||||
|
{
|
||||||
|
execute(sync_barrier, thread_index, std::forward<Task>(task));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Tasks>
|
||||||
|
static void call_jmp_table(barrier_t& sync_barrier, const EnumId current_task, const size_t thread_index, Tasks&&... tasks)
|
||||||
|
{
|
||||||
|
if (static_cast<EnumInt>(current_task) >= sizeof...(tasks))
|
||||||
|
BLT_UNREACHABLE;
|
||||||
|
(call(sync_barrier, current_task, thread_index, std::forward<Tasks>(tasks)) && ...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename EnumId>
|
||||||
|
class thread_manager_t
|
||||||
|
{
|
||||||
|
static_assert(std::is_enum_v<EnumId>, "Enum ID must be of enum type!");
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit thread_manager_t(const size_t thread_count, std::function<void(barrier_t&, EnumId, size_t)> task_func,
|
||||||
|
const bool will_main_block = true): barrier(thread_count), will_main_block(will_main_block)
|
||||||
|
{
|
||||||
|
thread_callable = [this, task_func = std::move(task_func)](const size_t thread_index)
|
||||||
|
{
|
||||||
|
while (should_run)
|
||||||
|
{
|
||||||
|
barrier.wait();
|
||||||
|
if (tasks_remaining > 0)
|
||||||
|
task_func(barrier, tasks.back(), thread_index);
|
||||||
|
barrier.wait();
|
||||||
|
if (thread_index == 0)
|
||||||
|
{
|
||||||
|
if (this->will_main_block)
|
||||||
|
{
|
||||||
|
tasks.pop_back();
|
||||||
|
--tasks_remaining;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::scoped_lock lock{task_lock};
|
||||||
|
tasks.pop_back();
|
||||||
|
--tasks_remaining;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (size_t i = 0; i < will_main_block ? thread_count - 1 : thread_count; ++i)
|
||||||
|
threads.emplace_back(thread_callable, will_main_block ? i + 1 : i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute() const
|
||||||
|
{
|
||||||
|
BLT_ASSERT(will_main_block &&
|
||||||
|
"You attempted to call this function without specifying that "
|
||||||
|
"you want an external blocking thread (try passing will_main_block = true)");
|
||||||
|
thread_callable(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_task(EnumId task)
|
||||||
|
{
|
||||||
|
if (will_main_block)
|
||||||
|
{
|
||||||
|
tasks.push_back(task);
|
||||||
|
++tasks_remaining;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(task_lock);
|
||||||
|
tasks.push_back(task);
|
||||||
|
++tasks_remaining;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_tasks_left()
|
||||||
|
{
|
||||||
|
if (will_main_block)
|
||||||
|
{
|
||||||
|
return !tasks.empty();
|
||||||
|
}
|
||||||
|
std::scoped_lock lock{task_lock};
|
||||||
|
return tasks.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
~thread_manager_t()
|
||||||
|
{
|
||||||
|
should_run = false;
|
||||||
|
for (auto& thread : threads)
|
||||||
|
{
|
||||||
|
if (thread.joinable())
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
[[nodiscard]] size_t thread_count() const
|
||||||
|
{
|
||||||
|
return will_main_block ? threads.size() + 1 : threads.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
blt::barrier_t barrier;
|
||||||
|
std::atomic_bool should_run = true;
|
||||||
|
bool will_main_block;
|
||||||
|
std::vector<EnumId> tasks;
|
||||||
|
std::atomic_uint64_t tasks_remaining = 0;
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
std::mutex task_lock;
|
||||||
|
|
||||||
|
std::function<void(size_t)> thread_callable;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //BLT_GP_THREADING_H
|
|
@ -24,21 +24,22 @@
|
||||||
#include <blt/gp/tree.h>
|
#include <blt/gp/tree.h>
|
||||||
#include <blt/gp/generators.h>
|
#include <blt/gp/generators.h>
|
||||||
#include <blt/std/expected.h>
|
#include <blt/std/expected.h>
|
||||||
|
#include <blt/meta/config_generator.h>
|
||||||
|
|
||||||
namespace blt::gp
|
namespace blt::gp
|
||||||
{
|
{
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
template<typename T>
|
template <typename T>
|
||||||
inline static constexpr double sum(const T& array)
|
inline static constexpr double sum(const T& array)
|
||||||
{
|
{
|
||||||
double init = 0.0;
|
double init = 0.0;
|
||||||
for (double i : array)
|
for (const double i : array)
|
||||||
init += i;
|
init += i;
|
||||||
return init;
|
return init;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<blt::size_t size, typename... Args>
|
template <size_t size, typename... Args>
|
||||||
static constexpr std::array<double, size> aggregate_array(Args... list)
|
static constexpr std::array<double, size> aggregate_array(Args... list)
|
||||||
{
|
{
|
||||||
std::array<double, size> data{list...};
|
std::array<double, size> data{list...};
|
||||||
|
@ -56,118 +57,203 @@ namespace blt::gp
|
||||||
|
|
||||||
class crossover_t
|
class crossover_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct crossover_point_t
|
struct point_info_t
|
||||||
{
|
{
|
||||||
blt::ptrdiff_t p1_crossover_point;
|
ptrdiff_t point;
|
||||||
blt::ptrdiff_t p2_crossover_point;
|
operator_info_t& type_operator_info;
|
||||||
};
|
};
|
||||||
struct config_t
|
|
||||||
{
|
|
||||||
// number of times crossover will try to pick a valid point in the tree. this is purely based on the return type of the operators
|
|
||||||
blt::u16 max_crossover_tries = 5;
|
|
||||||
// if we fail to find a point in the tree, should we search forward from the last point to the end of the operators?
|
|
||||||
bool should_crossover_try_forward = false;
|
|
||||||
// avoid selecting terminals when doing crossover
|
|
||||||
bool avoid_terminals = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
crossover_t() = default;
|
struct config_t
|
||||||
|
{
|
||||||
|
// number of times crossover will try to pick a valid point in the tree. this is purely based on the return type of the operators
|
||||||
|
u32 max_crossover_tries = 5;
|
||||||
|
// how many times the crossover function can fail before we will skip this operation.
|
||||||
|
u32 max_crossover_iterations = 10;
|
||||||
|
// if tree have fewer nodes than this number, they will not be considered for crossover
|
||||||
|
// should be at least 5 as crossover will not select the root node.
|
||||||
|
u32 min_tree_size = 5;
|
||||||
|
// used by the traverse version of get_crossover_point
|
||||||
|
// at each depth level, what chance do we have to exit with this as our point? or in other words what's the chance we continue traversing
|
||||||
|
// this is what this option configures.
|
||||||
|
f32 depth_multiplier = 0.5;
|
||||||
|
// how often should we select terminals over functions. By default, we only allow selection of terminals 10% of the time
|
||||||
|
// this applies to both types of crossover point functions. Traversal will use the parent if it should not pick a terminal.
|
||||||
|
f32 terminal_chance = 0.1;
|
||||||
|
// use traversal to select point instead of random selection
|
||||||
|
bool traverse = false;
|
||||||
|
|
||||||
explicit crossover_t(const config_t& config): config(config)
|
BLT_MAKE_SETTER_LVALUE(u32, max_crossover_tries);
|
||||||
{}
|
BLT_MAKE_SETTER_LVALUE(u32, max_crossover_iterations);
|
||||||
|
BLT_MAKE_SETTER_LVALUE(u32, min_tree_size);
|
||||||
|
BLT_MAKE_SETTER_LVALUE(f32, depth_multiplier);
|
||||||
|
BLT_MAKE_SETTER_LVALUE(f32, terminal_chance);
|
||||||
|
BLT_MAKE_SETTER_LVALUE(bool, traverse);
|
||||||
|
};
|
||||||
|
|
||||||
std::optional<crossover_t::crossover_point_t> get_crossover_point(gp_program& program, const tree_t& c1, const tree_t& c2) const;
|
explicit crossover_t(const config_t& config): config(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* child1 and child2 are copies of the parents, the result of selecting a crossover point and performing standard subtree crossover.
|
* Apply crossover to a set of parents. Note: c1 and c2 are already filled with thier respective parent's elements.
|
||||||
* the parents are not modified during this process
|
* @return true if the crossover succeeded, otherwise return false will erase progress.
|
||||||
* @param program reference to the global program container responsible for managing these trees
|
*/
|
||||||
* @param p1 reference to the first parent
|
virtual bool apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2) = 0;
|
||||||
* @param p2 reference to the second parent
|
|
||||||
* @return expected pair of child otherwise returns error enum
|
|
||||||
*/
|
|
||||||
virtual bool apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2); // NOLINT
|
|
||||||
|
|
||||||
virtual ~crossover_t() = default;
|
[[nodiscard]] const config_t& get_config() const
|
||||||
|
{
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
virtual ~crossover_t() = default;
|
||||||
config_t config;
|
|
||||||
|
protected:
|
||||||
|
config_t config;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for crossover which performs basic subtree crossover on two random nodes in the parent tree
|
||||||
|
*/
|
||||||
|
class subtree_crossover_t : public crossover_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct crossover_point_t
|
||||||
|
{
|
||||||
|
tree_t::subtree_point_t p1_crossover_point;
|
||||||
|
tree_t::subtree_point_t p2_crossover_point;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
subtree_crossover_t(): crossover_t(config_t{})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit subtree_crossover_t(const config_t& config): crossover_t(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<crossover_point_t> get_crossover_point(const tree_t& c1, const tree_t& c2) const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<crossover_point_t> get_crossover_point_traverse(const tree_t& c1, const tree_t& c2) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* child1 and child2 are copies of the parents, the result of selecting a crossover point and performing standard subtree crossover.
|
||||||
|
* the parents are not modified during this process
|
||||||
|
* @param program reference to the global program container responsible for managing these trees
|
||||||
|
* @param p1 reference to the first parent
|
||||||
|
* @param p2 reference to the second parent
|
||||||
|
* @param c1 reference to output child 1
|
||||||
|
* @param c2 reference to output child 2
|
||||||
|
* @return true if function succeeded, otherwise false
|
||||||
|
*/
|
||||||
|
virtual bool apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2) override; // NOLINT
|
||||||
|
|
||||||
|
~subtree_crossover_t() override = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
[[nodiscard]] std::optional<tree_t::subtree_point_t> get_point_traverse_retry(const tree_t& t, std::optional<type_id> type) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class one_point_crossover_t : public crossover_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
one_point_crossover_t(): crossover_t(config_t{})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit one_point_crossover_t(const config_t& config): crossover_t(config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class advanced_crossover_t : public crossover_t
|
||||||
|
{
|
||||||
|
advanced_crossover_t(): crossover_t(config_t{})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
bool apply(gp_program& program, const tree_t& p1, const tree_t& p2, tree_t& c1, tree_t& c2) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class mutation_t
|
class mutation_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct config_t
|
struct config_t
|
||||||
|
{
|
||||||
|
blt::size_t replacement_min_depth = 2;
|
||||||
|
blt::size_t replacement_max_depth = 6;
|
||||||
|
|
||||||
|
std::reference_wrapper<tree_generator_t> generator;
|
||||||
|
|
||||||
|
config_t(tree_generator_t& generator): generator(generator) // NOLINT
|
||||||
{
|
{
|
||||||
blt::size_t replacement_min_depth = 2;
|
}
|
||||||
blt::size_t replacement_max_depth = 6;
|
|
||||||
|
|
||||||
std::reference_wrapper<tree_generator_t> generator;
|
config_t();
|
||||||
|
};
|
||||||
|
|
||||||
config_t(tree_generator_t& generator): generator(generator) // NOLINT
|
mutation_t() = default;
|
||||||
{}
|
|
||||||
|
|
||||||
config_t();
|
explicit mutation_t(const config_t& config): config(config)
|
||||||
};
|
{
|
||||||
|
}
|
||||||
|
|
||||||
mutation_t() = default;
|
virtual bool apply(gp_program& program, const tree_t& p, tree_t& c);
|
||||||
|
|
||||||
explicit mutation_t(const config_t& config): config(config)
|
// returns the point after the mutation
|
||||||
{}
|
size_t mutate_point(gp_program& program, tree_t& c, tree_t::subtree_point_t node) const;
|
||||||
|
|
||||||
virtual bool apply(gp_program& program, const tree_t& p, tree_t& c);
|
virtual ~mutation_t() = default;
|
||||||
|
|
||||||
// returns the point after the mutation
|
protected:
|
||||||
blt::size_t mutate_point(gp_program& program, tree_t& c, blt::size_t node);
|
config_t config;
|
||||||
|
|
||||||
virtual ~mutation_t() = default;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
config_t config;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class advanced_mutation_t : public mutation_t
|
class advanced_mutation_t : public mutation_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class mutation_operator : blt::i32
|
enum class mutation_operator : i32
|
||||||
{
|
{
|
||||||
EXPRESSION, // Generate a new random expression
|
EXPRESSION, // Generate a new random expression
|
||||||
ADJUST, // adjust the value of the type. (if it is a function it will mutate it to a different one)
|
ADJUST, // adjust the value of the type. (if it is a function it will mutate it to a different one)
|
||||||
SUB_FUNC, // subexpression becomes argument to new random function. Other args are generated.
|
SUB_FUNC, // subexpression becomes argument to new random function. Other args are generated.
|
||||||
JUMP_FUNC, // subexpression becomes this new node. Other arguments discarded.
|
JUMP_FUNC, // subexpression becomes this new node. Other arguments discarded.
|
||||||
COPY, // node can become copy of another subexpression.
|
COPY, // node can become copy of another subexpression.
|
||||||
END, // helper
|
END, // helper
|
||||||
};
|
};
|
||||||
|
|
||||||
advanced_mutation_t() = default;
|
advanced_mutation_t() = default;
|
||||||
|
|
||||||
explicit advanced_mutation_t(const config_t& config): mutation_t(config)
|
explicit advanced_mutation_t(const config_t& config): mutation_t(config)
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
bool apply(gp_program& program, const tree_t& p, tree_t& c) final;
|
bool apply(gp_program& program, const tree_t& p, tree_t& c) final;
|
||||||
|
|
||||||
advanced_mutation_t& set_per_node_mutation_chance(double v)
|
advanced_mutation_t& set_per_node_mutation_chance(double v)
|
||||||
{
|
{
|
||||||
per_node_mutation_chance = v;
|
per_node_mutation_chance = v;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr auto operators_size = static_cast<blt::i32>(mutation_operator::END);
|
static constexpr auto operators_size = static_cast<blt::i32>(mutation_operator::END);
|
||||||
private:
|
|
||||||
// this value is adjusted inversely to the size of the tree.
|
|
||||||
double per_node_mutation_chance = 5.0;
|
|
||||||
|
|
||||||
static constexpr std::array<double, operators_size> mutation_operator_chances = detail::aggregate_array<operators_size>(
|
private:
|
||||||
0.25, // EXPRESSION
|
// this value is adjusted inversely to the size of the tree.
|
||||||
0.15, // ADJUST
|
double per_node_mutation_chance = 5.0;
|
||||||
0.01, // SUB_FUNC
|
|
||||||
0.01, // JUMP_FUNC
|
|
||||||
0.05 // COPY
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
static constexpr std::array<double, operators_size> mutation_operator_chances = detail::aggregate_array<operators_size>(
|
||||||
|
0.25, // EXPRESSION
|
||||||
|
0.20, // ADJUST
|
||||||
|
0.05, // SUB_FUNC
|
||||||
|
0.15, // JUMP_FUNC
|
||||||
|
0.10 // COPY
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //BLT_GP_TRANSFORMERS_H
|
#endif //BLT_GP_TRANSFORMERS_H
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -30,49 +30,56 @@
|
||||||
|
|
||||||
namespace blt::gp
|
namespace blt::gp
|
||||||
{
|
{
|
||||||
struct operator_id : integer_type<blt::size_t>
|
struct operator_id : integer_type<u64>
|
||||||
{
|
{
|
||||||
using integer_type<blt::size_t>::integer_type;
|
using integer_type::integer_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct type_id : integer_type<blt::size_t>
|
struct type_id : integer_type<u64>
|
||||||
{
|
{
|
||||||
using integer_type<blt::size_t>::integer_type;
|
using integer_type::integer_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
class type
|
class type
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
type() = default;
|
type() = default;
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
static type make_type(type_id id)
|
static type make_type(const type_id id)
|
||||||
{
|
{
|
||||||
return type(sizeof(T), id, blt::type_string<T>());
|
return type(stack_allocator::aligned_size<T>(), id, blt::type_string<T>(), detail::has_func_drop_v<detail::remove_cv_ref<T>>);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline blt::size_t size() const
|
[[nodiscard]] size_t size() const
|
||||||
{
|
{
|
||||||
return size_;
|
return size_;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline type_id id() const
|
[[nodiscard]] type_id id() const
|
||||||
{
|
{
|
||||||
return id_;
|
return id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline std::string_view name() const
|
[[nodiscard]] std::string_view name() const
|
||||||
{
|
{
|
||||||
return name_;
|
return name_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
[[nodiscard]] bool has_ephemeral_drop() const
|
||||||
type(size_t size, type_id id, std::string_view name): size_(size), id_(id), name_(name)
|
{
|
||||||
{}
|
return has_ephemeral_drop_;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
type(const size_t size, const type_id id, const std::string_view name, const bool has_ephemeral_drop): size_(size), id_(id), name_(name),
|
||||||
|
has_ephemeral_drop_(has_ephemeral_drop)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
blt::size_t size_{};
|
size_t size_{};
|
||||||
type_id id_{};
|
type_id id_{};
|
||||||
std::string name_{};
|
std::string name_{};
|
||||||
|
bool has_ephemeral_drop_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,57 +88,58 @@ namespace blt::gp
|
||||||
*/
|
*/
|
||||||
class type_provider
|
class type_provider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
type_provider() = default;
|
type_provider() = default;
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
inline void register_type()
|
void register_type()
|
||||||
{
|
{
|
||||||
if (has_type<T>())
|
if (has_type<T>())
|
||||||
return;
|
return;
|
||||||
auto t = type::make_type<T>(types.size());
|
auto t = type::make_type<T>(types.size());
|
||||||
types.insert({blt::type_string_raw<T>(), t});
|
types.insert({blt::type_string_raw<T>(), t});
|
||||||
types_from_id[t.id()] = t;
|
types_from_id[t.id()] = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
inline type get_type()
|
type get_type()
|
||||||
{
|
{
|
||||||
return types[blt::type_string_raw<T>()];
|
return types[blt::type_string_raw<T>()];
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
inline bool has_type(){
|
bool has_type()
|
||||||
return types.find(blt::type_string_raw<T>()) != types.end();
|
{
|
||||||
}
|
return types.find(blt::type_string_raw<T>()) != types.end();
|
||||||
|
}
|
||||||
|
|
||||||
inline type get_type(type_id id)
|
type get_type(type_id id)
|
||||||
{
|
{
|
||||||
return types_from_id[id];
|
return types_from_id[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is slow btw
|
* This function is slow btw
|
||||||
* @param engine
|
* @param engine
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
inline type select_type(std::mt19937_64& engine)
|
type select_type(std::mt19937_64& engine)
|
||||||
{
|
{
|
||||||
std::uniform_int_distribution dist(0ul, types.size() - 1);
|
std::uniform_int_distribution dist(0ul, types.size() - 1);
|
||||||
auto offset = dist(engine);
|
auto offset = dist(engine);
|
||||||
auto itr = types.begin();
|
auto itr = types.begin();
|
||||||
for ([[maybe_unused]] auto _ : blt::range(0ul, offset))
|
for ([[maybe_unused]] auto _ : blt::range(0ul, offset))
|
||||||
itr = itr++;
|
itr = itr++;
|
||||||
return itr->second;
|
return itr->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
blt::hashmap_t<std::string, type> types;
|
hashmap_t<std::string, type> types;
|
||||||
blt::expanding_buffer<type> types_from_id;
|
expanding_buffer<type> types_from_id;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template <>
|
||||||
struct std::hash<blt::gp::operator_id>
|
struct std::hash<blt::gp::operator_id>
|
||||||
{
|
{
|
||||||
std::size_t operator()(const blt::gp::operator_id& s) const noexcept
|
std::size_t operator()(const blt::gp::operator_id& s) const noexcept
|
||||||
|
@ -140,7 +148,7 @@ struct std::hash<blt::gp::operator_id>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template <>
|
||||||
struct std::hash<blt::gp::type_id>
|
struct std::hash<blt::gp::type_id>
|
||||||
{
|
{
|
||||||
std::size_t operator()(const blt::gp::type_id& s) const noexcept
|
std::size_t operator()(const blt::gp::type_id& s) const noexcept
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLT_GP_UTIL_META_H
|
||||||
|
#define BLT_GP_UTIL_META_H
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace blt::gp::detail
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
using remove_cv_ref = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||||
|
|
||||||
|
|
||||||
|
template <typename...>
|
||||||
|
struct first_arg;
|
||||||
|
|
||||||
|
template <typename First, typename... Args>
|
||||||
|
struct first_arg<First, Args...>
|
||||||
|
{
|
||||||
|
using type = First;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct first_arg<>
|
||||||
|
{
|
||||||
|
using type = void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <bool b, typename... types>
|
||||||
|
struct is_same;
|
||||||
|
|
||||||
|
template <typename... types>
|
||||||
|
struct is_same<true, types...> : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... types>
|
||||||
|
struct is_same<false, types...> : std::is_same<types...>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... types>
|
||||||
|
constexpr bool is_same_v = is_same<sizeof...(types) == 0, types...>::value;
|
||||||
|
|
||||||
|
struct empty_t
|
||||||
|
{
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //BLT_GP_UTIL_META_H
|
|
@ -0,0 +1,216 @@
|
||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BLT_GP_UTIL_STATISTICS_H
|
||||||
|
#define BLT_GP_UTIL_STATISTICS_H
|
||||||
|
|
||||||
|
#include <blt/gp/util/trackers.h>
|
||||||
|
#include <blt/gp/allocator.h>
|
||||||
|
#include <blt/gp/fwdecl.h>
|
||||||
|
|
||||||
|
namespace blt::gp
|
||||||
|
{
|
||||||
|
struct confusion_matrix_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
confusion_matrix_t() = default;
|
||||||
|
|
||||||
|
confusion_matrix_t& is_A_predicted_A()
|
||||||
|
{
|
||||||
|
++is_A_pred_A;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
confusion_matrix_t& is_A_predicted_B()
|
||||||
|
{
|
||||||
|
++is_A_pred_B;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
confusion_matrix_t& is_B_predicted_A()
|
||||||
|
{
|
||||||
|
++is_B_pred_A;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
confusion_matrix_t& is_B_predicted_B()
|
||||||
|
{
|
||||||
|
++is_B_pred_B;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
confusion_matrix_t& set_name_a(const std::string& name_a)
|
||||||
|
{
|
||||||
|
name_A = name_a;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
confusion_matrix_t& set_name_b(const std::string& name_b)
|
||||||
|
{
|
||||||
|
name_B = name_b;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u64 get_is_a_pred_a() const
|
||||||
|
{
|
||||||
|
return is_A_pred_A;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u64 get_is_a_pred_b() const
|
||||||
|
{
|
||||||
|
return is_A_pred_B;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u64 get_is_b_pred_b() const
|
||||||
|
{
|
||||||
|
return is_B_pred_B;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u64 get_is_b_pred_a() const
|
||||||
|
{
|
||||||
|
return is_B_pred_A;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u64 get_hits() const
|
||||||
|
{
|
||||||
|
return is_A_pred_A + is_B_pred_B;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u64 get_misses() const
|
||||||
|
{
|
||||||
|
return is_B_pred_A + is_A_pred_B;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u64 get_total() const
|
||||||
|
{
|
||||||
|
return get_hits() + get_misses();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] double get_percent_hit() const
|
||||||
|
{
|
||||||
|
return static_cast<double>(get_hits()) / static_cast<double>(get_total());
|
||||||
|
}
|
||||||
|
|
||||||
|
confusion_matrix_t& operator+=(const confusion_matrix_t& op)
|
||||||
|
{
|
||||||
|
is_A_pred_A += op.is_A_pred_A;
|
||||||
|
is_B_pred_A += op.is_B_pred_A;
|
||||||
|
is_A_pred_B += op.is_A_pred_B;
|
||||||
|
is_B_pred_B += op.is_B_pred_B;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
confusion_matrix_t& operator/=(const u64 val)
|
||||||
|
{
|
||||||
|
is_A_pred_A /= val;
|
||||||
|
is_B_pred_A /= val;
|
||||||
|
is_A_pred_B /= val;
|
||||||
|
is_B_pred_B /= val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend confusion_matrix_t operator+(const confusion_matrix_t& op1, const confusion_matrix_t& op2)
|
||||||
|
{
|
||||||
|
confusion_matrix_t result = op1;
|
||||||
|
result += op2;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend confusion_matrix_t operator/(const confusion_matrix_t& op1, const u64 val)
|
||||||
|
{
|
||||||
|
confusion_matrix_t result = op1;
|
||||||
|
result /= val;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator<(const confusion_matrix_t& a, const confusion_matrix_t& b)
|
||||||
|
{
|
||||||
|
return a.get_percent_hit() < b.get_percent_hit();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator>(const confusion_matrix_t& a, const confusion_matrix_t& b)
|
||||||
|
{
|
||||||
|
return a.get_percent_hit() > b.get_percent_hit();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::string pretty_print(const std::string& table_name = "Confusion Matrix") const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
u64 is_A_pred_A = 0;
|
||||||
|
u64 is_A_pred_B = 0;
|
||||||
|
u64 is_B_pred_B = 0;
|
||||||
|
u64 is_B_pred_A = 0;
|
||||||
|
std::string name_A = "A";
|
||||||
|
std::string name_B = "B";
|
||||||
|
};
|
||||||
|
|
||||||
|
struct population_stats
|
||||||
|
{
|
||||||
|
population_stats() = default;
|
||||||
|
|
||||||
|
population_stats(const population_stats& copy):
|
||||||
|
overall_fitness(copy.overall_fitness.load()), average_fitness(copy.average_fitness.load()), best_fitness(copy.best_fitness.load()),
|
||||||
|
worst_fitness(copy.worst_fitness.load())
|
||||||
|
{
|
||||||
|
normalized_fitness.reserve(copy.normalized_fitness.size());
|
||||||
|
for (auto v : copy.normalized_fitness)
|
||||||
|
normalized_fitness.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
population_stats(population_stats&& move) noexcept:
|
||||||
|
overall_fitness(move.overall_fitness.load()), average_fitness(move.average_fitness.load()), best_fitness(move.best_fitness.load()),
|
||||||
|
worst_fitness(move.worst_fitness.load()), normalized_fitness(std::move(move.normalized_fitness))
|
||||||
|
{
|
||||||
|
move.overall_fitness = 0;
|
||||||
|
move.average_fitness = 0;
|
||||||
|
move.best_fitness = 0;
|
||||||
|
move.worst_fitness = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::atomic<double> overall_fitness = 0;
|
||||||
|
std::atomic<double> average_fitness = 0;
|
||||||
|
std::atomic<double> best_fitness = 0;
|
||||||
|
std::atomic<double> worst_fitness = 1;
|
||||||
|
tracked_vector<double> normalized_fitness{};
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
overall_fitness = 0;
|
||||||
|
average_fitness = 0;
|
||||||
|
best_fitness = 0;
|
||||||
|
worst_fitness = 0;
|
||||||
|
normalized_fitness.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(const population_stats& a, const population_stats& b)
|
||||||
|
{
|
||||||
|
return a.overall_fitness.load(std::memory_order_relaxed) == b.overall_fitness.load(std::memory_order_relaxed) &&
|
||||||
|
a.average_fitness.load(std::memory_order_relaxed) == b.average_fitness.load(std::memory_order_relaxed) &&
|
||||||
|
a.best_fitness.load(std::memory_order_relaxed) == b.best_fitness.load(std::memory_order_relaxed) &&
|
||||||
|
a.worst_fitness.load(std::memory_order_relaxed) == b.worst_fitness.load(std::memory_order_relaxed) &&
|
||||||
|
a.normalized_fitness == b.normalized_fitness;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const population_stats& a, const population_stats& b)
|
||||||
|
{
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //BLT_GP_UTIL_STATISTICS_H
|
|
@ -316,14 +316,15 @@ namespace blt::gp
|
||||||
return secondary_value.load();
|
return secondary_value.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
call_data_t start_measurement()
|
call_data_t start_measurement() const
|
||||||
{
|
{
|
||||||
return {primary_calls.load(), 0};
|
return {primary_calls.load(), secondary_value.load()};
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop_measurement(call_data_t& data)
|
void stop_measurement(call_data_t& data) const
|
||||||
{
|
{
|
||||||
data.end_calls = primary_calls.load();
|
data.end_calls = primary_calls.load();
|
||||||
|
data.end_value = secondary_value.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
2
lib/blt
2
lib/blt
|
@ -1 +1 @@
|
||||||
Subproject commit 7198a8b0c32e35c9d80d8e44ff17c7199ddde6f8
|
Subproject commit f0fe0c1ceed644513eb8fa787aac3001a906d209
|
|
@ -1,41 +0,0 @@
|
||||||
Performance counter stats for './cmake-build-release/blt-symbolic-regression-example' (30 runs):
|
|
||||||
|
|
||||||
81,986,993,284 branches ( +- 15.89% ) (19.93%)
|
|
||||||
194,632,894 branch-misses # 0.24% of all branches ( +- 21.10% ) (19.84%)
|
|
||||||
32,561,539 cache-misses # 0.89% of all cache refs ( +- 10.21% ) (19.95%)
|
|
||||||
3,645,509,810 cache-references ( +- 15.93% ) (20.11%)
|
|
||||||
169,957,442,648 cycles ( +- 15.85% ) (20.26%)
|
|
||||||
426,558,894,577 instructions # 2.51 insn per cycle ( +- 16.24% ) (20.29%)
|
|
||||||
0 alignment-faults
|
|
||||||
9,103 cgroup-switches ( +- 13.62% )
|
|
||||||
52,586 faults ( +- 5.74% )
|
|
||||||
1,823,320,688 ns duration_time ( +- 12.76% )
|
|
||||||
41,213,439,537 ns user_time ( +- 3.68% )
|
|
||||||
219,435,124 ns system_time ( +- 2.44% )
|
|
||||||
132,928,139,347 L1-dcache-loads ( +- 15.55% ) (20.40%)
|
|
||||||
2,559,138,346 L1-dcache-load-misses # 1.93% of all L1-dcache accesses ( +- 15.53% ) (20.37%)
|
|
||||||
852,474,938 L1-dcache-prefetches ( +- 19.61% ) (20.44%)
|
|
||||||
1,035,909,753 L1-icache-loads ( +- 11.73% ) (20.45%)
|
|
||||||
1,451,589 L1-icache-load-misses # 0.14% of all L1-icache accesses ( +- 13.61% ) (20.50%)
|
|
||||||
37,722,800 dTLB-loads ( +- 14.93% ) (20.52%)
|
|
||||||
4,119,243 dTLB-load-misses # 10.92% of all dTLB cache accesses ( +- 10.99% ) (20.55%)
|
|
||||||
1,318,136 iTLB-loads ( +- 20.32% ) (20.51%)
|
|
||||||
367,939 iTLB-load-misses # 27.91% of all iTLB cache accesses ( +- 12.34% ) (20.42%)
|
|
||||||
2,730,214,946 l2_request_g1.all_no_prefetch ( +- 15.32% ) (20.43%)
|
|
||||||
52,586 page-faults ( +- 5.74% )
|
|
||||||
52,583 page-faults:u ( +- 5.75% )
|
|
||||||
3 page-faults:k ( +- 3.96% )
|
|
||||||
132,786,226,560 L1-dcache-loads ( +- 15.54% ) (20.33%)
|
|
||||||
2,581,181,694 L1-dcache-load-misses # 1.94% of all L1-dcache accesses ( +- 15.34% ) (20.26%)
|
|
||||||
<not supported> LLC-loads
|
|
||||||
<not supported> LLC-load-misses
|
|
||||||
1,021,814,075 L1-icache-loads ( +- 11.67% ) (20.19%)
|
|
||||||
1,376,958 L1-icache-load-misses # 0.13% of all L1-icache accesses ( +- 13.76% ) (20.09%)
|
|
||||||
38,065,494 dTLB-loads ( +- 14.76% ) (20.09%)
|
|
||||||
4,174,010 dTLB-load-misses # 11.06% of all dTLB cache accesses ( +- 10.90% ) (20.14%)
|
|
||||||
1,407,386 iTLB-loads ( +- 20.45% ) (20.09%)
|
|
||||||
338,781 iTLB-load-misses # 25.70% of all iTLB cache accesses ( +- 12.61% ) (20.05%)
|
|
||||||
873,873,406 L1-dcache-prefetches ( +- 19.41% ) (20.00%)
|
|
||||||
<not supported> L1-dcache-prefetch-misses
|
|
||||||
|
|
||||||
1.823 +- 0.233 seconds time elapsed ( +- 12.76% )
|
|
|
@ -17,7 +17,7 @@
|
||||||
*/
|
*/
|
||||||
#include <blt/gp/generators.h>
|
#include <blt/gp/generators.h>
|
||||||
#include <blt/gp/program.h>
|
#include <blt/gp/program.h>
|
||||||
#include <blt/std/logging.h>
|
#include <blt/logging/logging.h>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
|
||||||
namespace blt::gp
|
namespace blt::gp
|
||||||
|
@ -27,8 +27,8 @@ namespace blt::gp
|
||||||
|
|
||||||
struct stack
|
struct stack
|
||||||
{
|
{
|
||||||
blt::gp::operator_id id;
|
operator_id id;
|
||||||
blt::size_t depth;
|
size_t depth;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::stack<stack> get_initial_stack(gp_program& program, type_id root_type)
|
inline std::stack<stack> get_initial_stack(gp_program& program, type_id root_type)
|
||||||
|
@ -49,10 +49,10 @@ namespace blt::gp
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Func>
|
template<typename Func>
|
||||||
inline void create_tree(tree_t& tree, Func&& perChild, const generator_arguments& args)
|
void create_tree(tree_t& tree, Func&& perChild, const generator_arguments& args)
|
||||||
{
|
{
|
||||||
std::stack<stack> tree_generator = get_initial_stack(args.program, args.root_type);
|
std::stack<stack> tree_generator = get_initial_stack(args.program, args.root_type);
|
||||||
blt::size_t max_depth = 0;
|
size_t max_depth = 0;
|
||||||
|
|
||||||
while (!tree_generator.empty())
|
while (!tree_generator.empty())
|
||||||
{
|
{
|
||||||
|
@ -61,17 +61,15 @@ namespace blt::gp
|
||||||
|
|
||||||
auto& info = args.program.get_operator_info(top.id);
|
auto& info = args.program.get_operator_info(top.id);
|
||||||
|
|
||||||
tree.get_operations().emplace_back(
|
tree.emplace_operator(
|
||||||
args.program.get_typesystem().get_type(info.return_type).size(),
|
args.program.get_typesystem().get_type(info.return_type).size(),
|
||||||
top.id,
|
top.id,
|
||||||
args.program.is_operator_ephemeral(top.id));
|
args.program.is_operator_ephemeral(top.id),
|
||||||
|
args.program.get_operator_flags(top.id));
|
||||||
max_depth = std::max(max_depth, top.depth);
|
max_depth = std::max(max_depth, top.depth);
|
||||||
|
|
||||||
if (args.program.is_operator_ephemeral(top.id))
|
if (args.program.is_operator_ephemeral(top.id))
|
||||||
{
|
|
||||||
info.func(nullptr, tree.get_values(), tree.get_values());
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& child : info.argument_types)
|
for (const auto& child : info.argument_types)
|
||||||
std::forward<Func>(perChild)(args.program, tree_generator, child, top.depth + 1);
|
std::forward<Func>(perChild)(args.program, tree_generator, child, top.depth + 1);
|
||||||
|
|
194
src/program.cpp
194
src/program.cpp
|
@ -17,6 +17,13 @@
|
||||||
*/
|
*/
|
||||||
#include <blt/gp/program.h>
|
#include <blt/gp/program.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <blt/std/variant.h>
|
||||||
|
|
||||||
|
#ifndef BLT_ASSERT_RET
|
||||||
|
#define BLT_ASSERT_RET(expr) if (!(expr)) { return false; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BLT_READ(read_statement, size) do { auto read = read_statement; if (read != size) { return blt::gp::errors::serialization::invalid_read_t{read, size}; } } while (false)
|
||||||
|
|
||||||
namespace blt::gp
|
namespace blt::gp
|
||||||
{
|
{
|
||||||
|
@ -24,25 +31,28 @@ namespace blt::gp
|
||||||
// this is largely to not break the tests :3
|
// this is largely to not break the tests :3
|
||||||
// it's also to allow for quick setup of a gp program if you don't care how crossover or mutation is handled
|
// it's also to allow for quick setup of a gp program if you don't care how crossover or mutation is handled
|
||||||
static advanced_mutation_t s_mutator;
|
static advanced_mutation_t s_mutator;
|
||||||
static crossover_t s_crossover;
|
// static subtree_crossover_t s_crossover;
|
||||||
|
static one_point_crossover_t s_crossover;
|
||||||
static ramped_half_initializer_t s_init;
|
static ramped_half_initializer_t s_init;
|
||||||
|
|
||||||
prog_config_t::prog_config_t(): mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init)
|
prog_config_t::prog_config_t(): mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prog_config_t::prog_config_t(const std::reference_wrapper<population_initializer_t>& popInitializer):
|
prog_config_t::prog_config_t(const std::reference_wrapper<population_initializer_t>& popInitializer):
|
||||||
mutator(s_mutator), crossover(s_crossover), pop_initializer(popInitializer)
|
mutator(s_mutator), crossover(s_crossover), pop_initializer(popInitializer)
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
prog_config_t::prog_config_t(size_t populationSize, const std::reference_wrapper<population_initializer_t>& popInitializer):
|
prog_config_t::prog_config_t(size_t populationSize, const std::reference_wrapper<population_initializer_t>& popInitializer):
|
||||||
population_size(populationSize), mutator(s_mutator), crossover(s_crossover), pop_initializer(popInitializer)
|
population_size(populationSize), mutator(s_mutator), crossover(s_crossover), pop_initializer(popInitializer)
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
prog_config_t::prog_config_t(size_t populationSize):
|
prog_config_t::prog_config_t(size_t populationSize):
|
||||||
population_size(populationSize), mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init)
|
population_size(populationSize), mutator(s_mutator), crossover(s_crossover), pop_initializer(s_init)
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
random_t& gp_program::get_random() const
|
random_t& gp_program::get_random() const
|
||||||
{
|
{
|
||||||
|
@ -56,6 +66,171 @@ namespace blt::gp
|
||||||
return allocator;
|
return allocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gp_program::save_generation(fs::writer_t& writer)
|
||||||
|
{
|
||||||
|
const auto individuals = current_pop.get_individuals().size();
|
||||||
|
writer.write(&individuals, sizeof(individuals));
|
||||||
|
for (const auto& individual : current_pop.get_individuals())
|
||||||
|
{
|
||||||
|
writer.write(&individual.fitness, sizeof(individual.fitness));
|
||||||
|
individual.tree.to_file(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gp_program::load_generation(fs::reader_t& reader)
|
||||||
|
{
|
||||||
|
size_t individuals;
|
||||||
|
BLT_ASSERT_RET(reader.read(&individuals, sizeof(individuals)) == sizeof(individuals));
|
||||||
|
if (current_pop.get_individuals().size() != individuals)
|
||||||
|
{
|
||||||
|
for (size_t i = current_pop.get_individuals().size(); i < individuals; i++)
|
||||||
|
current_pop.get_individuals().emplace_back(tree_t{*this});
|
||||||
|
}
|
||||||
|
for (auto& individual : current_pop.get_individuals())
|
||||||
|
{
|
||||||
|
BLT_ASSERT_RET(reader.read(&individual.fitness, sizeof(individual.fitness)) == sizeof(individual.fitness));
|
||||||
|
individual.tree.clear(*this);
|
||||||
|
individual.tree.from_file(reader);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_stat(fs::writer_t& writer, const population_stats& stat)
|
||||||
|
{
|
||||||
|
const auto overall_fitness = stat.overall_fitness.load();
|
||||||
|
const auto average_fitness = stat.average_fitness.load();
|
||||||
|
const auto best_fitness = stat.best_fitness.load();
|
||||||
|
const auto worst_fitness = stat.worst_fitness.load();
|
||||||
|
writer.write(&overall_fitness, sizeof(overall_fitness));
|
||||||
|
writer.write(&average_fitness, sizeof(average_fitness));
|
||||||
|
writer.write(&best_fitness, sizeof(best_fitness));
|
||||||
|
writer.write(&worst_fitness, sizeof(worst_fitness));
|
||||||
|
const size_t fitness_count = stat.normalized_fitness.size();
|
||||||
|
writer.write(&fitness_count, sizeof(fitness_count));
|
||||||
|
for (const auto& fitness : stat.normalized_fitness)
|
||||||
|
writer.write(&fitness, sizeof(fitness));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load_stat(fs::reader_t& reader, population_stats& stat)
|
||||||
|
{
|
||||||
|
BLT_ASSERT_RET(reader.read(&stat.overall_fitness, sizeof(stat.overall_fitness)) == sizeof(stat.overall_fitness));
|
||||||
|
BLT_ASSERT_RET(reader.read(&stat.average_fitness, sizeof(stat.average_fitness)) == sizeof(stat.average_fitness));
|
||||||
|
BLT_ASSERT_RET(reader.read(&stat.best_fitness, sizeof(stat.best_fitness)) == sizeof(stat.best_fitness));
|
||||||
|
BLT_ASSERT_RET(reader.read(&stat.worst_fitness, sizeof(stat.worst_fitness)) == sizeof(stat.worst_fitness));
|
||||||
|
size_t fitness_count;
|
||||||
|
BLT_ASSERT_RET(reader.read(&fitness_count, sizeof(fitness_count)) == sizeof(size_t));
|
||||||
|
stat.normalized_fitness.resize(fitness_count);
|
||||||
|
for (auto& fitness : stat.normalized_fitness)
|
||||||
|
BLT_ASSERT_RET(reader.read(&fitness, sizeof(fitness)) == sizeof(fitness));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gp_program::save_state(fs::writer_t& writer)
|
||||||
|
{
|
||||||
|
const size_t operator_count = storage.operators.size();
|
||||||
|
writer.write(&operator_count, sizeof(operator_count));
|
||||||
|
for (const auto& [i, op] : enumerate(storage.operators))
|
||||||
|
{
|
||||||
|
writer.write(&i, sizeof(i));
|
||||||
|
bool has_name = storage.names[i].has_value();
|
||||||
|
writer.write(&has_name, sizeof(has_name));
|
||||||
|
if (has_name)
|
||||||
|
{
|
||||||
|
auto size = storage.names[i]->size();
|
||||||
|
writer.write(&size, sizeof(size));
|
||||||
|
writer.write(storage.names[i]->data(), size);
|
||||||
|
}
|
||||||
|
writer.write(&storage.operator_metadata[i].arg_size_bytes, sizeof(storage.operator_metadata[i].arg_size_bytes));
|
||||||
|
writer.write(&storage.operator_metadata[i].return_size_bytes, sizeof(storage.operator_metadata[i].return_size_bytes));
|
||||||
|
writer.write(&op.argc, sizeof(op.argc));
|
||||||
|
writer.write(&op.return_type, sizeof(op.return_type));
|
||||||
|
const size_t argc_type_count = op.argument_types.size();
|
||||||
|
writer.write(&argc_type_count, sizeof(argc_type_count));
|
||||||
|
for (const auto argument : op.argument_types)
|
||||||
|
writer.write(&argument, sizeof(argument));
|
||||||
|
}
|
||||||
|
const size_t history_count = statistic_history.size();
|
||||||
|
writer.write(&history_count, sizeof(history_count));
|
||||||
|
for (const auto& stat : statistic_history)
|
||||||
|
write_stat(writer, stat);
|
||||||
|
write_stat(writer, current_stats);
|
||||||
|
save_generation(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<errors::serialization::serializer_error_t> gp_program::load_state(fs::reader_t& reader)
|
||||||
|
{
|
||||||
|
size_t operator_count;
|
||||||
|
BLT_READ(reader.read(&operator_count, sizeof(operator_count)), sizeof(operator_count));
|
||||||
|
if (operator_count != storage.operators.size())
|
||||||
|
return errors::serialization::unexpected_size_t{operator_count, storage.operators.size()};
|
||||||
|
for (size_t i = 0; i < operator_count; i++)
|
||||||
|
{
|
||||||
|
size_t expected_i;
|
||||||
|
BLT_READ(reader.read(&expected_i, sizeof(expected_i)), sizeof(expected_i));
|
||||||
|
if (expected_i != i)
|
||||||
|
return errors::serialization::invalid_operator_id_t{i, expected_i};
|
||||||
|
bool has_name;
|
||||||
|
BLT_READ(reader.read(&has_name, sizeof(has_name)), sizeof(has_name));
|
||||||
|
if (has_name)
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
BLT_READ(reader.read(&size, sizeof(size)), sizeof(size));
|
||||||
|
std::string name;
|
||||||
|
name.resize(size);
|
||||||
|
BLT_READ(reader.read(name.data(), size), static_cast<i64>(size));
|
||||||
|
if (!storage.names[i].has_value())
|
||||||
|
return errors::serialization::invalid_name_t{i, name, "NO NAME"};
|
||||||
|
if (name != *storage.names[i])
|
||||||
|
return errors::serialization::invalid_name_t{i, name, std::string{*storage.names[i]}};
|
||||||
|
const auto& op = storage.operators[i];
|
||||||
|
const auto& op_meta = storage.operator_metadata[i];
|
||||||
|
|
||||||
|
decltype(std::declval<decltype(storage.operator_metadata)::value_type>().arg_size_bytes) arg_size_bytes;
|
||||||
|
decltype(std::declval<decltype(storage.operator_metadata)::value_type>().return_size_bytes) return_size_bytes;
|
||||||
|
BLT_READ(reader.read(&arg_size_bytes, sizeof(arg_size_bytes)), sizeof(arg_size_bytes));
|
||||||
|
BLT_READ(reader.read(&return_size_bytes, sizeof(return_size_bytes)), sizeof(return_size_bytes));
|
||||||
|
|
||||||
|
if (op_meta.arg_size_bytes != arg_size_bytes)
|
||||||
|
return errors::serialization::mismatched_bytes_t{i, arg_size_bytes, op_meta.arg_size_bytes};
|
||||||
|
|
||||||
|
if (op_meta.return_size_bytes != return_size_bytes)
|
||||||
|
return errors::serialization::mismatched_bytes_t{i, return_size_bytes, op_meta.return_size_bytes};
|
||||||
|
|
||||||
|
argc_t argc;
|
||||||
|
BLT_READ(reader.read(&argc, sizeof(argc)), sizeof(argc));
|
||||||
|
if (argc.argc != op.argc.argc)
|
||||||
|
return errors::serialization::mismatched_argc_t{i, argc.argc, op.argc.argc};
|
||||||
|
if (argc.argc_context != op.argc.argc_context)
|
||||||
|
return errors::serialization::mismatched_argc_t{i, argc.argc_context, op.argc.argc_context};
|
||||||
|
|
||||||
|
type_id return_type;
|
||||||
|
BLT_READ(reader.read(&return_type, sizeof(return_type)), sizeof(return_type));
|
||||||
|
if (return_type != op.return_type)
|
||||||
|
return errors::serialization::mismatched_return_type_t{i, return_type, op.return_type};
|
||||||
|
size_t arg_type_count;
|
||||||
|
BLT_READ(reader.read(&arg_type_count, sizeof(arg_type_count)), sizeof(return_type));
|
||||||
|
if (arg_type_count != op.argument_types.size())
|
||||||
|
return errors::serialization::unexpected_size_t{arg_type_count, op.argument_types.size()};
|
||||||
|
for (size_t j = 0; j < arg_type_count; j++)
|
||||||
|
{
|
||||||
|
type_id type;
|
||||||
|
BLT_READ(reader.read(&type, sizeof(type)), sizeof(type));
|
||||||
|
if (type != op.argument_types[j])
|
||||||
|
return errors::serialization::mismatched_arg_type_t{i, j, type, op.argument_types[j]};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size_t history_count;
|
||||||
|
BLT_READ(reader.read(&history_count, sizeof(history_count)), sizeof(history_count));
|
||||||
|
statistic_history.resize(history_count);
|
||||||
|
for (size_t i = 0; i < history_count; i++)
|
||||||
|
load_stat(reader, statistic_history[i]);
|
||||||
|
load_stat(reader, current_stats);
|
||||||
|
load_generation(reader);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void gp_program::create_threads()
|
void gp_program::create_threads()
|
||||||
{
|
{
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
#ifdef BLT_TRACK_ALLOCATIONS
|
||||||
|
@ -67,7 +242,8 @@ namespace blt::gp
|
||||||
// main thread is thread0
|
// main thread is thread0
|
||||||
for (blt::size_t i = 1; i < config.threads; i++)
|
for (blt::size_t i = 1; i < config.threads; i++)
|
||||||
{
|
{
|
||||||
thread_helper.threads.emplace_back(new std::thread([i, this]() {
|
thread_helper.threads.emplace_back(new std::thread([i, this]()
|
||||||
|
{
|
||||||
#ifdef BLT_TRACK_ALLOCATIONS
|
#ifdef BLT_TRACK_ALLOCATIONS
|
||||||
tracker.reserve();
|
tracker.reserve();
|
||||||
tracker.await_thread_loading_complete(config.threads);
|
tracker.await_thread_loading_complete(config.threads);
|
||||||
|
|
|
@ -20,37 +20,34 @@
|
||||||
|
|
||||||
namespace blt::gp
|
namespace blt::gp
|
||||||
{
|
{
|
||||||
|
void select_best_t::pre_process(gp_program&, population_t&)
|
||||||
|
{
|
||||||
|
// std::sort(pop.begin(), pop.end(), [](const auto& a, const auto& b)
|
||||||
|
// {
|
||||||
|
// return a.fitness.adjusted_fitness > b.fitness.adjusted_fitness;
|
||||||
|
// });
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
const tree_t& select_best_t::select(gp_program&, const population_t& pop)
|
const tree_t& select_best_t::select(gp_program&, const population_t& pop)
|
||||||
{
|
{
|
||||||
auto& first = pop.get_individuals()[0];
|
const auto size = pop.get_individuals().size();
|
||||||
double best_fitness = first.fitness.adjusted_fitness;
|
return pop.get_individuals()[index.fetch_add(1, std::memory_order_relaxed) % size].tree;
|
||||||
const tree_t* tree = &first.tree;
|
}
|
||||||
for (auto& ind : pop.get_individuals())
|
|
||||||
{
|
void select_worst_t::pre_process(gp_program&, population_t&)
|
||||||
if (ind.fitness.adjusted_fitness > best_fitness)
|
{
|
||||||
{
|
// std::sort(pop.begin(), pop.end(), [](const auto& a, const auto& b)
|
||||||
best_fitness = ind.fitness.adjusted_fitness;
|
// {
|
||||||
tree = &ind.tree;
|
// return a.fitness.adjusted_fitness < b.fitness.adjusted_fitness;
|
||||||
}
|
// });
|
||||||
}
|
index = 0;
|
||||||
return *tree;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const tree_t& select_worst_t::select(gp_program&, const population_t& pop)
|
const tree_t& select_worst_t::select(gp_program&, const population_t& pop)
|
||||||
{
|
{
|
||||||
auto& first = pop.get_individuals()[0];
|
const auto size = pop.get_individuals().size();
|
||||||
double worst_fitness = first.fitness.adjusted_fitness;
|
return pop.get_individuals()[(size - 1) - (index.fetch_add(1, std::memory_order_relaxed) % size)].tree;
|
||||||
const tree_t* tree = &first.tree;
|
|
||||||
for (auto& ind : pop.get_individuals())
|
|
||||||
{
|
|
||||||
if (ind.fitness.adjusted_fitness < worst_fitness)
|
|
||||||
{
|
|
||||||
worst_fitness = ind.fitness.adjusted_fitness;
|
|
||||||
tree = &ind.tree;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return *tree;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const tree_t& select_random_t::select(gp_program& program, const population_t& pop)
|
const tree_t& select_random_t::select(gp_program& program, const population_t& pop)
|
||||||
|
@ -60,11 +57,20 @@ namespace blt::gp
|
||||||
|
|
||||||
const tree_t& select_tournament_t::select(gp_program& program, const population_t& pop)
|
const tree_t& select_tournament_t::select(gp_program& program, const population_t& pop)
|
||||||
{
|
{
|
||||||
blt::u64 best = program.get_random().get_u64(0, pop.get_individuals().size());
|
thread_local hashset_t<u64> already_selected;
|
||||||
|
already_selected.clear();
|
||||||
auto& i_ref = pop.get_individuals();
|
auto& i_ref = pop.get_individuals();
|
||||||
for (blt::size_t i = 0; i < selection_size; i++)
|
|
||||||
|
u64 best = program.get_random().get_u64(0, pop.get_individuals().size());
|
||||||
|
for (size_t i = 0; i < std::min(selection_size, pop.get_individuals().size()); i++)
|
||||||
{
|
{
|
||||||
auto sel_point = program.get_random().get_u64(0ul, pop.get_individuals().size());
|
u64 sel_point;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
sel_point = program.get_random().get_u64(0ul, pop.get_individuals().size());;
|
||||||
|
}
|
||||||
|
while (already_selected.contains(sel_point));
|
||||||
|
already_selected.insert(sel_point);
|
||||||
if (i_ref[sel_point].fitness.adjusted_fitness > i_ref[best].fitness.adjusted_fitness)
|
if (i_ref[sel_point].fitness.adjusted_fitness > i_ref[best].fitness.adjusted_fitness)
|
||||||
best = sel_point;
|
best = sel_point;
|
||||||
}
|
}
|
||||||
|
@ -81,7 +87,8 @@ namespace blt::gp
|
||||||
{
|
{
|
||||||
if (choice <= stats.normalized_fitness[index])
|
if (choice <= stats.normalized_fitness[index])
|
||||||
return ref.tree;
|
return ref.tree;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (choice > stats.normalized_fitness[index - 1] && choice <= stats.normalized_fitness[index])
|
if (choice > stats.normalized_fitness[index - 1] && choice <= stats.normalized_fitness[index])
|
||||||
return ref.tree;
|
return ref.tree;
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* <Short Description>
|
||||||
|
* Copyright (C) 2025 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <blt/gp/sync.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <blt/gp/program.h>
|
||||||
|
#include <blt/std/time.h>
|
||||||
|
|
||||||
|
namespace blt::gp
|
||||||
|
{
|
||||||
|
struct global_sync_state_t
|
||||||
|
{
|
||||||
|
std::vector<sync_t*> syncs;
|
||||||
|
std::mutex mutex;
|
||||||
|
std::thread* thread = nullptr;
|
||||||
|
std::atomic_bool should_run = true;
|
||||||
|
std::condition_variable condition_variable;
|
||||||
|
|
||||||
|
void add(sync_t* sync)
|
||||||
|
{
|
||||||
|
if (thread == nullptr)
|
||||||
|
{
|
||||||
|
thread = new std::thread([this]()
|
||||||
|
{
|
||||||
|
while (should_run)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(mutex);
|
||||||
|
condition_variable.wait_for(lock, std::chrono::milliseconds(100));
|
||||||
|
const auto current_time = system::getCurrentTimeMilliseconds();
|
||||||
|
for (const auto& sync : syncs)
|
||||||
|
sync->trigger(current_time);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
std::scoped_lock lock(mutex);
|
||||||
|
syncs.push_back(sync);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(const sync_t* sync)
|
||||||
|
{
|
||||||
|
if (thread == nullptr)
|
||||||
|
{
|
||||||
|
BLT_WARN("Tried to remove sync from global sync state, but no thread was running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::unique_lock lock(mutex);
|
||||||
|
const auto iter = std::find(syncs.begin(), syncs.end(), sync);
|
||||||
|
std::iter_swap(iter, syncs.end() - 1);
|
||||||
|
syncs.pop_back();
|
||||||
|
if (syncs.empty())
|
||||||
|
{
|
||||||
|
lock.unlock();
|
||||||
|
should_run = false;
|
||||||
|
condition_variable.notify_all();
|
||||||
|
thread->join();
|
||||||
|
delete thread;
|
||||||
|
thread = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
global_sync_state_t& get_state()
|
||||||
|
{
|
||||||
|
static global_sync_state_t state;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_t::sync_t(gp_program& program, fs::writer_t& writer): m_program(&program), m_writer(&writer)
|
||||||
|
{
|
||||||
|
get_state().add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sync_t::trigger(const u64 current_time) const
|
||||||
|
{
|
||||||
|
if ((m_timer_seconds && (current_time % *m_timer_seconds == 0)) || (m_generations && (m_program->get_current_generation() % *m_generations ==
|
||||||
|
0)))
|
||||||
|
{
|
||||||
|
if (m_reset_to_start_of_file)
|
||||||
|
m_writer->seek(0, fs::writer_t::seek_origin::seek_set);
|
||||||
|
if (m_whole_program)
|
||||||
|
m_program->save_state(*m_writer);
|
||||||
|
else
|
||||||
|
m_program->save_generation(*m_writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_t::~sync_t()
|
||||||
|
{
|
||||||
|
get_state().remove(this);
|
||||||
|
}
|
||||||
|
}
|
1145
src/transformers.cpp
1145
src/transformers.cpp
File diff suppressed because it is too large
Load Diff
724
src/tree.cpp
724
src/tree.cpp
|
@ -18,23 +18,19 @@
|
||||||
#include <blt/gp/tree.h>
|
#include <blt/gp/tree.h>
|
||||||
#include <blt/gp/stack.h>
|
#include <blt/gp/stack.h>
|
||||||
#include <blt/std/assert.h>
|
#include <blt/std/assert.h>
|
||||||
#include <blt/std/logging.h>
|
#include <blt/logging/logging.h>
|
||||||
#include <blt/gp/program.h>
|
#include <blt/gp/program.h>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
|
||||||
namespace blt::gp
|
namespace blt::gp
|
||||||
{
|
{
|
||||||
// this one will copy previous bytes over
|
template <typename>
|
||||||
template<typename T>
|
static u8* get_thread_pointer_for_size(const size_t bytes)
|
||||||
blt::span<blt::u8> get_pointer_for_size(blt::size_t size)
|
|
||||||
{
|
{
|
||||||
static blt::span<blt::u8> buffer{nullptr, 0};
|
thread_local expanding_buffer<u8> buffer;
|
||||||
if (buffer.size() < size)
|
if (bytes > buffer.size())
|
||||||
{
|
buffer.resize(bytes);
|
||||||
delete[] buffer.data();
|
return buffer.data();
|
||||||
buffer = {new blt::u8[size], size};
|
|
||||||
}
|
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& create_indent(std::ostream& out, blt::size_t amount, bool pretty_print)
|
std::ostream& create_indent(std::ostream& out, blt::size_t amount, bool pretty_print)
|
||||||
|
@ -58,7 +54,16 @@ namespace blt::gp
|
||||||
return "(" + std::string(program.get_typesystem().get_type(id).name()) + ")";
|
return "(" + std::string(program.get_typesystem().get_type(id).name()) + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void tree_t::print(gp_program& program, std::ostream& out, bool print_literals, bool pretty_print, bool include_types) const
|
void tree_t::byte_only_transaction_t::move(const size_t bytes_to_move)
|
||||||
|
{
|
||||||
|
bytes = bytes_to_move;
|
||||||
|
data = get_thread_pointer_for_size<struct move_tempoary_bytes>(bytes);
|
||||||
|
tree.values.copy_to(data, bytes);
|
||||||
|
tree.values.pop_bytes(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tree_t::print(std::ostream& out, const bool print_literals, const bool pretty_print, const bool include_types,
|
||||||
|
const ptrdiff_t marked_index) const
|
||||||
{
|
{
|
||||||
std::stack<blt::size_t> arguments_left;
|
std::stack<blt::size_t> arguments_left;
|
||||||
blt::size_t indent = 0;
|
blt::size_t indent = 0;
|
||||||
|
@ -72,34 +77,41 @@ namespace blt::gp
|
||||||
// reverse the order of the stack
|
// reverse the order of the stack
|
||||||
for (const auto& v : operations)
|
for (const auto& v : operations)
|
||||||
{
|
{
|
||||||
if (v.is_value)
|
if (v.is_value())
|
||||||
copy.transfer_bytes(reversed, v.type_size);
|
copy.transfer_bytes(reversed, v.type_size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const auto& v : operations)
|
for (const auto& [i, v] : enumerate(operations))
|
||||||
{
|
{
|
||||||
auto info = program.get_operator_info(v.id);
|
auto info = m_program->get_operator_info(v.id());
|
||||||
auto name = program.get_name(v.id) ? program.get_name(v.id).value() : "NULL";
|
const auto name = m_program->get_name(v.id()) ? m_program->get_name(v.id()).value() : "NULL";
|
||||||
auto return_type = get_return_type(program, info.return_type, include_types);
|
auto return_type = get_return_type(*m_program, info.return_type, include_types);
|
||||||
|
if (static_cast<ptrdiff_t>(i) == marked_index)
|
||||||
|
{
|
||||||
|
out << "[ERROR OCCURRED HERE] -> ";
|
||||||
|
}
|
||||||
if (info.argc.argc > 0)
|
if (info.argc.argc > 0)
|
||||||
{
|
{
|
||||||
create_indent(out, indent, pretty_print) << "(";
|
create_indent(out, indent, pretty_print) << "(";
|
||||||
indent++;
|
indent++;
|
||||||
arguments_left.emplace(info.argc.argc);
|
arguments_left.emplace(info.argc.argc);
|
||||||
out << name << return_type << end_indent(pretty_print);
|
out << name << return_type << end_indent(pretty_print);
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (print_literals)
|
if (print_literals)
|
||||||
{
|
{
|
||||||
create_indent(out, indent, pretty_print);
|
create_indent(out, indent, pretty_print);
|
||||||
if (program.is_operator_ephemeral(v.id))
|
if (m_program->is_operator_ephemeral(v.id()))
|
||||||
{
|
{
|
||||||
program.get_print_func(v.id)(out, reversed);
|
m_program->get_print_func(v.id())(out, reversed);
|
||||||
reversed.pop_bytes(stack_allocator::aligned_size(v.type_size));
|
reversed.pop_bytes(v.type_size());
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
out << name;
|
out << name;
|
||||||
out << return_type << end_indent(pretty_print);
|
out << return_type << end_indent(pretty_print);
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
create_indent(out, indent, pretty_print) << name << return_type << end_indent(pretty_print);
|
create_indent(out, indent, pretty_print) << name << return_type << end_indent(pretty_print);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +124,8 @@ namespace blt::gp
|
||||||
indent--;
|
indent--;
|
||||||
create_indent(out, indent, pretty_print) << ")" << end_indent(pretty_print);
|
create_indent(out, indent, pretty_print) << ")" << end_indent(pretty_print);
|
||||||
continue;
|
continue;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (!pretty_print)
|
if (!pretty_print)
|
||||||
out << " ";
|
out << " ";
|
||||||
|
@ -130,7 +143,8 @@ namespace blt::gp
|
||||||
indent--;
|
indent--;
|
||||||
create_indent(out, indent, pretty_print) << ")" << end_indent(pretty_print);
|
create_indent(out, indent, pretty_print) << ")" << end_indent(pretty_print);
|
||||||
continue;
|
continue;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
BLT_ERROR("Failed to print tree correctly!");
|
BLT_ERROR("Failed to print tree correctly!");
|
||||||
break;
|
break;
|
||||||
|
@ -140,17 +154,20 @@ namespace blt::gp
|
||||||
out << '\n';
|
out << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
blt::size_t tree_t::get_depth(gp_program& program)
|
size_t tree_t::get_depth(gp_program& program) const
|
||||||
{
|
{
|
||||||
blt::size_t depth = 0;
|
size_t depth = 0;
|
||||||
|
|
||||||
auto operations_stack = operations;
|
auto operations_stack = operations;
|
||||||
tracked_vector<blt::size_t> values_process;
|
thread_local tracked_vector<size_t> values_process;
|
||||||
tracked_vector<blt::size_t> value_stack;
|
thread_local tracked_vector<size_t> value_stack;
|
||||||
|
|
||||||
|
values_process.clear();
|
||||||
|
value_stack.clear();
|
||||||
|
|
||||||
for (const auto& op : operations_stack)
|
for (const auto& op : operations_stack)
|
||||||
{
|
{
|
||||||
if (op.is_value)
|
if (op.is_value())
|
||||||
value_stack.push_back(1);
|
value_stack.push_back(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +176,7 @@ namespace blt::gp
|
||||||
auto operation = operations_stack.back();
|
auto operation = operations_stack.back();
|
||||||
// keep the last value in the stack on the process stack stored in the eval context, this way it can be accessed easily.
|
// keep the last value in the stack on the process stack stored in the eval context, this way it can be accessed easily.
|
||||||
operations_stack.pop_back();
|
operations_stack.pop_back();
|
||||||
if (operation.is_value)
|
if (operation.is_value())
|
||||||
{
|
{
|
||||||
auto d = value_stack.back();
|
auto d = value_stack.back();
|
||||||
depth = std::max(depth, d);
|
depth = std::max(depth, d);
|
||||||
|
@ -167,145 +184,632 @@ namespace blt::gp
|
||||||
value_stack.pop_back();
|
value_stack.pop_back();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
blt::size_t local_depth = 0;
|
size_t local_depth = 0;
|
||||||
for (blt::size_t i = 0; i < program.get_operator_info(operation.id).argc.argc; i++)
|
for (size_t i = 0; i < program.get_operator_info(operation.id()).argc.argc; i++)
|
||||||
{
|
{
|
||||||
local_depth = std::max(local_depth, values_process.back());
|
local_depth = std::max(local_depth, values_process.back());
|
||||||
values_process.pop_back();
|
values_process.pop_back();
|
||||||
}
|
}
|
||||||
value_stack.push_back(local_depth + 1);
|
value_stack.push_back(local_depth + 1);
|
||||||
operations_stack.emplace_back(operation.type_size, operation.id, true);
|
operations_stack.emplace_back(operation.type_size(), operation.id(), true, program.get_operator_flags(operation.id()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return depth;
|
return depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
blt::ptrdiff_t tree_t::find_endpoint(gp_program& program, blt::ptrdiff_t index) const
|
tree_t::subtree_point_t tree_t::select_subtree(const double terminal_chance) const
|
||||||
{
|
{
|
||||||
blt::i64 children_left = 0;
|
do
|
||||||
|
{
|
||||||
|
const auto point = m_program->get_random().get_u64(0, operations.size());
|
||||||
|
const auto& info = m_program->get_operator_info(operations[point].id());
|
||||||
|
if (!info.argc.is_terminal())
|
||||||
|
return {static_cast<ptrdiff_t>(point), info.return_type};
|
||||||
|
if (m_program->get_random().choice(terminal_chance))
|
||||||
|
return {static_cast<ptrdiff_t>(point), info.return_type};
|
||||||
|
}
|
||||||
|
while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<tree_t::subtree_point_t> tree_t::select_subtree(const type_id type, const u32 max_tries, const double terminal_chance) const
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < max_tries; ++i)
|
||||||
|
{
|
||||||
|
if (const auto tree = select_subtree(terminal_chance); tree.type == type)
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_t::subtree_point_t tree_t::select_subtree_traverse(const double terminal_chance, const double depth_multiplier) const
|
||||||
|
{
|
||||||
|
size_t index = 0;
|
||||||
|
double depth = 0;
|
||||||
|
double exit_chance = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const auto& info = m_program->get_operator_info(operations[index].id());
|
||||||
|
if (info.argc.is_terminal())
|
||||||
|
{
|
||||||
|
if (m_program->get_random().choice(terminal_chance))
|
||||||
|
return {static_cast<ptrdiff_t>(index), info.return_type};
|
||||||
|
index = 0;
|
||||||
|
depth = 0;
|
||||||
|
exit_chance = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (m_program->get_random().choice(exit_chance))
|
||||||
|
return {static_cast<ptrdiff_t>(index), info.return_type};
|
||||||
|
|
||||||
|
const auto child = m_program->get_random().get_u32(0, info.argc.argc);
|
||||||
|
index++;
|
||||||
|
for (u32 i = 0; i < child; i++)
|
||||||
|
index = find_endpoint(static_cast<ptrdiff_t>(index));
|
||||||
|
|
||||||
|
++depth;
|
||||||
|
exit_chance = 1.0 - (1.0 / (1 + depth * depth_multiplier * 0.5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<tree_t::subtree_point_t> tree_t::select_subtree_traverse(const type_id type, const u32 max_tries, const double terminal_chance,
|
||||||
|
const double depth_multiplier) const
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < max_tries; ++i)
|
||||||
|
{
|
||||||
|
if (const auto tree = select_subtree_traverse(terminal_chance, depth_multiplier); tree.type == type)
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void tree_t::copy_subtree(const subtree_point_t point, const ptrdiff_t extent, tracked_vector<op_container_t>& operators, stack_allocator& stack)
|
||||||
|
{
|
||||||
|
const auto point_begin_itr = operations.begin() + point.pos;
|
||||||
|
const auto point_end_itr = operations.begin() + extent;
|
||||||
|
|
||||||
|
const size_t after_bytes = accumulate_type_sizes(point_end_itr, operations.end());
|
||||||
|
|
||||||
|
const size_t ops = std::distance(point_begin_itr, point_end_itr);
|
||||||
|
operators.reserve(operators.size() + ops);
|
||||||
|
// TODO something better!
|
||||||
|
for (size_t i = 0; i < ops; ++i)
|
||||||
|
operators.emplace_back(0, 0, false, operator_special_flags{});
|
||||||
|
size_t for_bytes = 0;
|
||||||
|
size_t pos = 0;
|
||||||
|
for (auto& it : iterate(point_begin_itr, point_end_itr).rev())
|
||||||
|
{
|
||||||
|
if (it.is_value())
|
||||||
|
{
|
||||||
|
for_bytes += it.type_size();
|
||||||
|
if (it.get_flags().is_ephemeral() && it.has_ephemeral_drop())
|
||||||
|
{
|
||||||
|
auto [_, ptr] = values.access_pointer(for_bytes + after_bytes, it.type_size());
|
||||||
|
++*ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operators[operators.size() - 1 - (pos++)] = it;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.copy_from(values, for_bytes, after_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tree_t::swap_subtrees(const child_t our_subtree, tree_t& other_tree, const child_t other_subtree)
|
||||||
|
{
|
||||||
|
const auto c1_subtree_begin_itr = operations.begin() + our_subtree.start;
|
||||||
|
const auto c1_subtree_end_itr = operations.begin() + our_subtree.end;
|
||||||
|
|
||||||
|
const auto c2_subtree_begin_itr = other_tree.operations.begin() + other_subtree.start;
|
||||||
|
const auto c2_subtree_end_itr = other_tree.operations.begin() + other_subtree.end;
|
||||||
|
|
||||||
|
thread_local tracked_vector<op_container_t> c1_subtree_operators;
|
||||||
|
thread_local tracked_vector<op_container_t> c2_subtree_operators;
|
||||||
|
c1_subtree_operators.clear();
|
||||||
|
c2_subtree_operators.clear();
|
||||||
|
|
||||||
|
c1_subtree_operators.reserve(std::distance(c1_subtree_begin_itr, c1_subtree_end_itr));
|
||||||
|
c2_subtree_operators.reserve(std::distance(c2_subtree_begin_itr, c2_subtree_end_itr));
|
||||||
|
|
||||||
|
// i don't think this is required for swapping values, since the total number of additions is net zero
|
||||||
|
// the tree isn't destroyed at any point.
|
||||||
|
|
||||||
|
size_t c1_subtree_bytes = 0;
|
||||||
|
for (const auto& it : iterate(c1_subtree_begin_itr, c1_subtree_end_itr))
|
||||||
|
{
|
||||||
|
if (it.is_value())
|
||||||
|
{
|
||||||
|
// if (it.get_flags().is_ephemeral() && it.has_ephemeral_drop())
|
||||||
|
// {
|
||||||
|
// auto& ptr = values.access_pointer_forward(for_our_bytes, it.type_size());
|
||||||
|
// ++*ptr;
|
||||||
|
// }
|
||||||
|
c1_subtree_bytes += it.type_size();
|
||||||
|
}
|
||||||
|
c1_subtree_operators.push_back(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t c2_subtree_bytes = 0;
|
||||||
|
for (const auto& it : iterate(c2_subtree_begin_itr, c2_subtree_end_itr))
|
||||||
|
{
|
||||||
|
if (it.is_value())
|
||||||
|
{
|
||||||
|
// if (it.get_flags().is_ephemeral() && it.has_ephemeral_drop())
|
||||||
|
// {
|
||||||
|
// auto& ptr = values.access_pointer_forward(for_other_bytes, it.type_size());
|
||||||
|
// ++*ptr;
|
||||||
|
// }
|
||||||
|
c2_subtree_bytes += it.type_size();
|
||||||
|
}
|
||||||
|
c2_subtree_operators.push_back(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t c1_stack_after_bytes = accumulate_type_sizes(c1_subtree_end_itr, operations.end());
|
||||||
|
const size_t c2_stack_after_bytes = accumulate_type_sizes(c2_subtree_end_itr, other_tree.operations.end());
|
||||||
|
const auto c1_total = static_cast<ptrdiff_t>(c1_stack_after_bytes + c1_subtree_bytes);
|
||||||
|
const auto c2_total = static_cast<ptrdiff_t>(c2_stack_after_bytes + c2_subtree_bytes);
|
||||||
|
const auto copy_ptr_c1 = get_thread_pointer_for_size<struct c1_t>(c1_total);
|
||||||
|
const auto copy_ptr_c2 = get_thread_pointer_for_size<struct c2_t>(c2_total);
|
||||||
|
|
||||||
|
values.reserve(values.stored() - c1_subtree_bytes + c2_subtree_bytes);
|
||||||
|
other_tree.values.reserve(other_tree.values.stored() - c2_subtree_bytes + c1_subtree_bytes);
|
||||||
|
|
||||||
|
values.copy_to(copy_ptr_c1, c1_total);
|
||||||
|
values.pop_bytes(c1_total);
|
||||||
|
|
||||||
|
other_tree.values.copy_to(copy_ptr_c2, c2_total);
|
||||||
|
other_tree.values.pop_bytes(c2_total);
|
||||||
|
|
||||||
|
other_tree.values.copy_from(copy_ptr_c1, c1_subtree_bytes);
|
||||||
|
other_tree.values.copy_from(copy_ptr_c2 + c2_subtree_bytes, c2_stack_after_bytes);
|
||||||
|
|
||||||
|
values.copy_from(copy_ptr_c2, c2_subtree_bytes);
|
||||||
|
values.copy_from(copy_ptr_c1 + c1_subtree_bytes, c1_stack_after_bytes);
|
||||||
|
|
||||||
|
// now swap the operators
|
||||||
|
// auto insert_point_c1 = c1_subtree_begin_itr - 1;
|
||||||
|
// auto insert_point_c2 = c2_subtree_begin_itr - 1;
|
||||||
|
|
||||||
|
// invalidates [begin, end()) so the insert points should be fine
|
||||||
|
auto insert_point_c1 = operations.erase(c1_subtree_begin_itr, c1_subtree_end_itr);
|
||||||
|
auto insert_point_c2 = other_tree.operations.erase(c2_subtree_begin_itr, c2_subtree_end_itr);
|
||||||
|
|
||||||
|
operations.insert(insert_point_c1, c2_subtree_operators.begin(), c2_subtree_operators.end());
|
||||||
|
other_tree.operations.insert(insert_point_c2, c1_subtree_operators.begin(), c1_subtree_operators.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tree_t::swap_subtrees(const subtree_point_t our_subtree, tree_t& other_tree, const subtree_point_t other_subtree)
|
||||||
|
{
|
||||||
|
swap_subtrees(child_t{our_subtree.pos, find_endpoint(our_subtree.pos)}, other_tree,
|
||||||
|
child_t{other_subtree.pos, find_endpoint(other_subtree.pos)});
|
||||||
|
}
|
||||||
|
|
||||||
|
void tree_t::replace_subtree(const subtree_point_t point, const ptrdiff_t extent, tree_t& other_tree)
|
||||||
|
{
|
||||||
|
const auto point_begin_itr = operations.begin() + point.pos;
|
||||||
|
const auto point_end_itr = operations.begin() + extent;
|
||||||
|
|
||||||
|
const size_t after_bytes = accumulate_type_sizes(point_end_itr, operations.end());
|
||||||
|
|
||||||
|
size_t for_bytes = 0;
|
||||||
|
for (auto& it : iterate(point_begin_itr, point_end_itr).rev())
|
||||||
|
{
|
||||||
|
if (it.is_value())
|
||||||
|
{
|
||||||
|
for_bytes += it.type_size();
|
||||||
|
if (it.get_flags().is_ephemeral() && it.has_ephemeral_drop())
|
||||||
|
{
|
||||||
|
auto [val, ptr] = values.access_pointer(for_bytes + after_bytes, it.type_size());
|
||||||
|
--*ptr;
|
||||||
|
if (*ptr == 0)
|
||||||
|
handle_ptr_empty(ptr, val, it.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto insert = operations.erase(point_begin_itr, point_end_itr);
|
||||||
|
|
||||||
|
const auto ptr = get_thread_pointer_for_size<struct replace>(after_bytes);
|
||||||
|
values.copy_to(ptr, after_bytes);
|
||||||
|
values.pop_bytes(after_bytes + for_bytes);
|
||||||
|
|
||||||
|
size_t copy_bytes = 0;
|
||||||
|
for (const auto& v : other_tree.operations)
|
||||||
|
{
|
||||||
|
if (v.is_value())
|
||||||
|
{
|
||||||
|
if (v.get_flags().is_ephemeral() && v.has_ephemeral_drop())
|
||||||
|
{
|
||||||
|
auto [_, pointer] = other_tree.values.access_pointer_forward(copy_bytes, v.type_size());
|
||||||
|
++*pointer;
|
||||||
|
}
|
||||||
|
copy_bytes += v.type_size();
|
||||||
|
}
|
||||||
|
insert = ++operations.emplace(insert, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
values.insert(other_tree.values);
|
||||||
|
values.copy_from(ptr, after_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tree_t::delete_subtree(const subtree_point_t point, const ptrdiff_t extent)
|
||||||
|
{
|
||||||
|
const auto point_begin_itr = operations.begin() + point.pos;
|
||||||
|
const auto point_end_itr = operations.begin() + extent;
|
||||||
|
|
||||||
|
const size_t after_bytes = accumulate_type_sizes(point_end_itr, operations.end());
|
||||||
|
|
||||||
|
size_t for_bytes = 0;
|
||||||
|
for (auto& it : iterate(point_begin_itr, point_end_itr).rev())
|
||||||
|
{
|
||||||
|
if (it.is_value())
|
||||||
|
{
|
||||||
|
for_bytes += it.type_size();
|
||||||
|
if (it.get_flags().is_ephemeral() && it.has_ephemeral_drop())
|
||||||
|
{
|
||||||
|
auto [val, ptr] = values.access_pointer(for_bytes + after_bytes, it.type_size());
|
||||||
|
--*ptr;
|
||||||
|
if (*ptr == 0)
|
||||||
|
handle_ptr_empty(ptr, val, it.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operations.erase(point_begin_itr, point_end_itr);
|
||||||
|
|
||||||
|
const auto ptr = get_thread_pointer_for_size<struct replace>(after_bytes);
|
||||||
|
values.copy_to(ptr, after_bytes);
|
||||||
|
values.pop_bytes(after_bytes + for_bytes);
|
||||||
|
values.copy_from(ptr, after_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptrdiff_t tree_t::insert_subtree(const subtree_point_t point, tree_t& other_tree)
|
||||||
|
{
|
||||||
|
const size_t after_bytes = accumulate_type_sizes(operations.begin() + point.pos, operations.end());
|
||||||
|
byte_only_transaction_t transaction{*this, after_bytes};
|
||||||
|
|
||||||
|
auto insert = operations.begin() + point.pos;
|
||||||
|
size_t bytes = 0;
|
||||||
|
for (auto& it : iterate(other_tree.operations).rev())
|
||||||
|
{
|
||||||
|
if (it.is_value())
|
||||||
|
{
|
||||||
|
bytes += it.type_size();
|
||||||
|
if (it.get_flags().is_ephemeral() && it.has_ephemeral_drop())
|
||||||
|
{
|
||||||
|
auto [_, ptr] = other_tree.values.access_pointer(bytes, it.type_size());
|
||||||
|
++*ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insert = operations.insert(insert, it);
|
||||||
|
}
|
||||||
|
values.insert(other_tree.values);
|
||||||
|
|
||||||
|
return static_cast<ptrdiff_t>(point.pos + other_tree.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ptrdiff_t tree_t::find_endpoint(ptrdiff_t start) const
|
||||||
|
{
|
||||||
|
i64 children_left = 0;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
const auto& type = program.get_operator_info(operations[index].id);
|
const auto& type = m_program->get_operator_info(operations[start].id());
|
||||||
// this is a child to someone
|
// this is a child to someone
|
||||||
if (children_left != 0)
|
if (children_left != 0)
|
||||||
children_left--;
|
children_left--;
|
||||||
if (type.argc.argc > 0)
|
if (type.argc.argc > 0)
|
||||||
children_left += type.argc.argc;
|
children_left += type.argc.argc;
|
||||||
index++;
|
start++;
|
||||||
} while (children_left > 0);
|
}
|
||||||
|
while (children_left > 0);
|
||||||
|
|
||||||
return index;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function doesn't work!
|
tree_t& tree_t::get_thread_local(gp_program& program)
|
||||||
blt::ptrdiff_t tree_t::find_parent(gp_program& program, blt::ptrdiff_t index) const
|
|
||||||
{
|
{
|
||||||
blt::i64 children_left = 0;
|
thread_local tree_t tree{program};
|
||||||
do
|
tree.clear(program);
|
||||||
{
|
return tree;
|
||||||
if (index == 0)
|
|
||||||
return 0;
|
|
||||||
const auto& type = program.get_operator_info(operations[index].id);
|
|
||||||
if (type.argc.argc > 0)
|
|
||||||
children_left -= type.argc.argc;
|
|
||||||
children_left++;
|
|
||||||
if (children_left <= 0)
|
|
||||||
break;
|
|
||||||
--index;
|
|
||||||
} while (true);
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tree_t::check(gp_program& program, void* context) const
|
void tree_t::handle_operator_inserted(const op_container_t& op)
|
||||||
{
|
{
|
||||||
blt::size_t bytes_expected = 0;
|
if (m_program->is_operator_ephemeral(op.id()))
|
||||||
auto bytes_size = values.size().total_used_bytes;
|
|
||||||
|
|
||||||
for (const auto& op : get_operations())
|
|
||||||
{
|
{
|
||||||
if (op.is_value)
|
// Ephemeral values have corresponding insertions into the stack
|
||||||
bytes_expected += stack_allocator::aligned_size(op.type_size);
|
m_program->get_operator_info(op.id()).func(nullptr, values, values);
|
||||||
|
if (m_program->operator_has_ephemeral_drop(op.id()))
|
||||||
|
{
|
||||||
|
auto [_, ptr] = values.access_pointer(op.type_size(), op.type_size());
|
||||||
|
ptr = new std::atomic_uint64_t(1);
|
||||||
|
ptr.bit(0, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tree_t::handle_ptr_empty(const mem::pointer_storage<std::atomic_uint64_t>& ptr, u8* data, const operator_id id) const
|
||||||
|
{
|
||||||
|
m_program->get_destroy_func(id)(detail::destroy_t::RETURN, data);
|
||||||
|
delete ptr.get();
|
||||||
|
// BLT_INFO("Deleting pointer!");
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluation_context& tree_t::evaluate(void* ptr) const
|
||||||
|
{
|
||||||
|
return m_program->get_eval_func()(*this, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tree_t::check(void* context) const
|
||||||
|
{
|
||||||
|
size_t bytes_expected = 0;
|
||||||
|
const auto bytes_size = values.stored();
|
||||||
|
|
||||||
|
for (const auto& op : operations)
|
||||||
|
{
|
||||||
|
if (op.is_value())
|
||||||
|
bytes_expected += op.type_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytes_expected != bytes_size)
|
if (bytes_expected != bytes_size)
|
||||||
{
|
{
|
||||||
BLT_WARN_STREAM << "Stack state: " << values.size() << "\n";
|
BLT_ERROR("Stack state: Stored: {}; Capacity: {}; Remainder: {}", values.stored(), values.capacity(), values.remainder());
|
||||||
BLT_WARN("Child tree bytes %ld vs expected %ld, difference: %ld", bytes_size, bytes_expected,
|
BLT_ERROR("Child tree bytes {} vs expected {}, difference: {}", bytes_size, bytes_expected,
|
||||||
static_cast<blt::ptrdiff_t>(bytes_expected) - static_cast<blt::ptrdiff_t>(bytes_size));
|
static_cast<ptrdiff_t>(bytes_expected) - static_cast<ptrdiff_t>(bytes_size));
|
||||||
BLT_WARN("Amount of bytes in stack doesn't match the number of bytes expected for the operations");
|
BLT_ERROR("Amount of bytes in stack doesn't match the number of bytes expected for the operations");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy the initial values
|
size_t total_produced = 0;
|
||||||
evaluation_context results{};
|
size_t total_consumed = 0;
|
||||||
|
size_t index = 0;
|
||||||
|
|
||||||
auto value_stack = values;
|
try
|
||||||
auto& values_process = results.values;
|
|
||||||
|
|
||||||
blt::size_t total_produced = 0;
|
|
||||||
blt::size_t total_consumed = 0;
|
|
||||||
|
|
||||||
for (const auto& operation : blt::reverse_iterate(operations.begin(), operations.end()))
|
|
||||||
{
|
{
|
||||||
if (operation.is_value)
|
// copy the initial values
|
||||||
|
evaluation_context results{};
|
||||||
|
|
||||||
|
auto value_stack = values;
|
||||||
|
auto& values_process = results.values;
|
||||||
|
|
||||||
|
for (const auto& operation : iterate(operations).rev())
|
||||||
{
|
{
|
||||||
value_stack.transfer_bytes(values_process, operation.type_size);
|
++index;
|
||||||
total_produced += stack_allocator::aligned_size(operation.type_size);
|
if (operation.is_value())
|
||||||
continue;
|
{
|
||||||
|
value_stack.transfer_bytes(values_process, operation.type_size());
|
||||||
|
total_produced += operation.type_size();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto& info = m_program->get_operator_info(operation.id());
|
||||||
|
for (auto& arg : info.argument_types)
|
||||||
|
total_consumed += m_program->get_typesystem().get_type(arg).size();
|
||||||
|
m_program->get_operator_info(operation.id()).func(context, values_process, values_process);
|
||||||
|
total_produced += m_program->get_typesystem().get_type(info.return_type).size();
|
||||||
}
|
}
|
||||||
auto& info = program.get_operator_info(operation.id);
|
|
||||||
for (auto& arg : info.argument_types)
|
|
||||||
total_consumed += stack_allocator::aligned_size(program.get_typesystem().get_type(arg).size());
|
|
||||||
program.get_operator_info(operation.id).func(context, values_process, values_process);
|
|
||||||
total_produced += stack_allocator::aligned_size(program.get_typesystem().get_type(info.return_type).size());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto v1 = results.values.bytes_in_head();
|
const auto v1 = static_cast<ptrdiff_t>(results.values.stored());
|
||||||
auto v2 = static_cast<blt::ptrdiff_t>(stack_allocator::aligned_size(operations.front().type_size));
|
const auto v2 = static_cast<ptrdiff_t>(operations.front().type_size());
|
||||||
if (v1 != v2)
|
|
||||||
|
// ephemeral don't need to be dropped as there are no copies which matter when checking the tree
|
||||||
|
if (!operations.front().get_flags().is_ephemeral())
|
||||||
|
m_program->get_destroy_func(operations.front().id())(detail::destroy_t::RETURN, results.values.from(operations.front().type_size()));
|
||||||
|
if (v1 != v2)
|
||||||
|
{
|
||||||
|
const auto vd = std::abs(v1 - v2);
|
||||||
|
BLT_ERROR("found {} bytes expected {} bytes, total difference: {}", v1, v2, vd);
|
||||||
|
BLT_ERROR("Total Produced {} || Total Consumed {} || Total Difference {}", total_produced, total_consumed,
|
||||||
|
std::abs(static_cast<blt::ptrdiff_t>(total_produced) - static_cast<blt::ptrdiff_t>(total_consumed)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
auto vd = std::abs(v1 - v2);
|
BLT_ERROR("Exception occurred \"{}\"", e.what());
|
||||||
BLT_ERROR("found %ld bytes expected %ld bytes, total difference: %ld", v1, v2, vd);
|
BLT_ERROR("Total Produced {} || Total Consumed {} || Total Difference {}", total_produced, total_consumed,
|
||||||
BLT_ERROR("Total Produced %ld || Total Consumed %ld || Total Difference %ld", total_produced, total_consumed,
|
|
||||||
std::abs(static_cast<blt::ptrdiff_t>(total_produced) - static_cast<blt::ptrdiff_t>(total_consumed)));
|
std::abs(static_cast<blt::ptrdiff_t>(total_produced) - static_cast<blt::ptrdiff_t>(total_consumed)));
|
||||||
|
BLT_ERROR("We failed at index {}", index);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tree_t::find_child_extends(gp_program& program, tracked_vector<child_t>& vec, blt::size_t parent_node, blt::size_t argc) const
|
void tree_t::find_child_extends(tracked_vector<child_t>& vec, const size_t parent_node, const size_t argc) const
|
||||||
{
|
{
|
||||||
|
BLT_ASSERT_MSG(vec.empty(), "Vector to find_child_extends should be empty!");
|
||||||
while (vec.size() < argc)
|
while (vec.size() < argc)
|
||||||
{
|
{
|
||||||
auto current_point = vec.size();
|
const auto current_point = vec.size();
|
||||||
child_t prev{};
|
child_t prev; // NOLINT
|
||||||
if (current_point == 0)
|
if (current_point == 0)
|
||||||
{
|
{
|
||||||
// first child.
|
// first child.
|
||||||
prev = {static_cast<blt::ptrdiff_t>(parent_node + 1),
|
prev = {
|
||||||
find_endpoint(program, static_cast<blt::ptrdiff_t>(parent_node + 1))};
|
static_cast<ptrdiff_t>(parent_node + 1),
|
||||||
|
find_endpoint(static_cast<ptrdiff_t>(parent_node + 1))
|
||||||
|
};
|
||||||
vec.push_back(prev);
|
vec.push_back(prev);
|
||||||
continue;
|
continue;
|
||||||
} else
|
}
|
||||||
prev = vec[current_point - 1];
|
prev = vec[current_point - 1];
|
||||||
child_t next = {prev.end, find_endpoint(program, prev.end)};
|
child_t next = {prev.end, find_endpoint(prev.end)};
|
||||||
vec.push_back(next);
|
vec.push_back(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tree_t::tree_t(gp_program& program): func(&program.get_eval_func())
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void tree_t::clear(gp_program& program)
|
void tree_t::clear(gp_program& program)
|
||||||
{
|
{
|
||||||
auto* f = &program.get_eval_func();
|
auto* f = &program;
|
||||||
if (f != func)
|
if (&program != m_program)
|
||||||
func = f;
|
m_program = f;
|
||||||
|
size_t total_bytes = 0;
|
||||||
|
for (const auto& op : iterate(operations))
|
||||||
|
{
|
||||||
|
if (op.is_value())
|
||||||
|
{
|
||||||
|
if (op.get_flags().is_ephemeral() && op.has_ephemeral_drop())
|
||||||
|
{
|
||||||
|
auto [val, ptr] = values.access_pointer_forward(total_bytes, op.type_size());
|
||||||
|
--*ptr;
|
||||||
|
if (*ptr == 0)
|
||||||
|
handle_ptr_empty(ptr, val, op.id());
|
||||||
|
}
|
||||||
|
total_bytes += op.type_size();
|
||||||
|
}
|
||||||
|
}
|
||||||
operations.clear();
|
operations.clear();
|
||||||
values.reset();
|
values.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tree_t::insert_operator(const size_t index, const op_container_t& container)
|
||||||
|
{
|
||||||
|
if (container.get_flags().is_ephemeral())
|
||||||
|
{
|
||||||
|
byte_only_transaction_t move{*this, total_value_bytes(index)};
|
||||||
|
handle_operator_inserted(container);
|
||||||
|
}
|
||||||
|
operations.insert(operations.begin() + static_cast<ptrdiff_t>(index), container);
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_t::subtree_point_t tree_t::subtree_from_point(ptrdiff_t point) const
|
||||||
|
{
|
||||||
|
return {point, m_program->get_operator_info(operations[point].id()).return_type};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t tree_t::required_size() const
|
||||||
|
{
|
||||||
|
// 2 size_t used to store expected_length of operations + size of the values stack
|
||||||
|
return 2 * sizeof(size_t) + operations.size() * sizeof(size_t) + values.stored();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tree_t::to_byte_array(std::byte* out) const
|
||||||
|
{
|
||||||
|
const auto op_size = operations.size();
|
||||||
|
std::memcpy(out, &op_size, sizeof(size_t));
|
||||||
|
out += sizeof(size_t);
|
||||||
|
for (const auto& op : operations)
|
||||||
|
{
|
||||||
|
constexpr auto size_of_op = sizeof(operator_id);
|
||||||
|
auto id = op.id();
|
||||||
|
std::memcpy(out, &id, size_of_op);
|
||||||
|
out += size_of_op;
|
||||||
|
}
|
||||||
|
const auto val_size = values.stored();
|
||||||
|
std::memcpy(out, &val_size, sizeof(size_t));
|
||||||
|
out += sizeof(size_t);
|
||||||
|
std::memcpy(out, values.data(), val_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tree_t::to_file(fs::writer_t& file) const
|
||||||
|
{
|
||||||
|
const auto op_size = operations.size();
|
||||||
|
BLT_ASSERT(file.write(&op_size, sizeof(size_t)) == sizeof(size_t));
|
||||||
|
for (const auto& op : operations)
|
||||||
|
{
|
||||||
|
auto id = op.id();
|
||||||
|
file.write(&id, sizeof(operator_id));
|
||||||
|
}
|
||||||
|
const auto val_size = values.stored();
|
||||||
|
BLT_ASSERT(file.write(&val_size, sizeof(size_t)) == sizeof(size_t));
|
||||||
|
BLT_ASSERT(file.write(values.data(), val_size) == static_cast<i64>(val_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tree_t::from_byte_array(const std::byte* in)
|
||||||
|
{
|
||||||
|
size_t ops_to_read;
|
||||||
|
std::memcpy(&ops_to_read, in, sizeof(size_t));
|
||||||
|
in += sizeof(size_t);
|
||||||
|
operations.reserve(ops_to_read);
|
||||||
|
for (size_t i = 0; i < ops_to_read; i++)
|
||||||
|
{
|
||||||
|
operator_id id;
|
||||||
|
std::memcpy(&id, in, sizeof(operator_id));
|
||||||
|
in += sizeof(operator_id);
|
||||||
|
operations.emplace_back(
|
||||||
|
m_program->get_typesystem().get_type(m_program->get_operator_info(id).return_type).size(),
|
||||||
|
id,
|
||||||
|
m_program->is_operator_ephemeral(id),
|
||||||
|
m_program->get_operator_flags(id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
size_t val_size;
|
||||||
|
std::memcpy(&val_size, in, sizeof(size_t));
|
||||||
|
in += sizeof(size_t);
|
||||||
|
// TODO replace instances of u8 that are used to alias types with the proper std::byte
|
||||||
|
values.copy_from(reinterpret_cast<const u8*>(in), val_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tree_t::from_file(fs::reader_t& file)
|
||||||
|
{
|
||||||
|
size_t ops_to_read;
|
||||||
|
BLT_ASSERT(file.read(&ops_to_read, sizeof(size_t)) == sizeof(size_t));
|
||||||
|
operations.reserve(ops_to_read);
|
||||||
|
for (size_t i = 0; i < ops_to_read; i++)
|
||||||
|
{
|
||||||
|
operator_id id;
|
||||||
|
BLT_ASSERT(file.read(&id, sizeof(operator_id)) == sizeof(operator_id));
|
||||||
|
operations.emplace_back(
|
||||||
|
m_program->get_typesystem().get_type(m_program->get_operator_info(id).return_type).size(),
|
||||||
|
id,
|
||||||
|
m_program->is_operator_ephemeral(id),
|
||||||
|
m_program->get_operator_flags(id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
size_t bytes_in_head;
|
||||||
|
BLT_ASSERT(file.read(&bytes_in_head, sizeof(size_t)) == sizeof(size_t));
|
||||||
|
values.resize(bytes_in_head);
|
||||||
|
BLT_ASSERT(file.read(values.data(), bytes_in_head) == static_cast<i64>(bytes_in_head));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tree_t::modify_operator(const size_t point, operator_id new_id, std::optional<type_id> return_type)
|
||||||
|
{
|
||||||
|
if (!return_type)
|
||||||
|
return_type = m_program->get_operator_info(new_id).return_type;
|
||||||
|
byte_only_transaction_t move_data{*this};
|
||||||
|
if (operations[point].is_value())
|
||||||
|
{
|
||||||
|
const size_t after_bytes = accumulate_type_sizes(operations.begin() + static_cast<ptrdiff_t>(point) + 1, operations.end());
|
||||||
|
move_data.move(after_bytes);
|
||||||
|
if (operations[point].get_flags().is_ephemeral() && operations[point].has_ephemeral_drop())
|
||||||
|
{
|
||||||
|
auto [val, ptr] = values.access_pointer(operations[point].type_size(), operations[point].type_size());
|
||||||
|
--*ptr;
|
||||||
|
if (*ptr == 0)
|
||||||
|
handle_ptr_empty(ptr, val, operations[point].id());
|
||||||
|
}
|
||||||
|
values.pop_bytes(operations[point].type_size());
|
||||||
|
}
|
||||||
|
operations[point] = {
|
||||||
|
m_program->get_typesystem().get_type(*return_type).size(),
|
||||||
|
new_id,
|
||||||
|
m_program->is_operator_ephemeral(new_id),
|
||||||
|
m_program->get_operator_flags(new_id)
|
||||||
|
};
|
||||||
|
if (operations[point].get_flags().is_ephemeral())
|
||||||
|
{
|
||||||
|
if (move_data.empty())
|
||||||
|
{
|
||||||
|
const size_t after_bytes = accumulate_type_sizes(operations.begin() + static_cast<ptrdiff_t>(point) + 1, operations.end());
|
||||||
|
move_data.move(after_bytes);
|
||||||
|
}
|
||||||
|
handle_operator_inserted(operations[point]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const tree_t& a, const tree_t& b)
|
||||||
|
{
|
||||||
|
if (a.operations.size() != b.operations.size())
|
||||||
|
return false;
|
||||||
|
if (a.values.stored() != b.values.stored())
|
||||||
|
return false;
|
||||||
|
return std::equal(a.operations.begin(), a.operations.end(), b.operations.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const op_container_t& a, const op_container_t& b)
|
||||||
|
{
|
||||||
|
return a.id() == b.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const individual_t& a, const individual_t& b)
|
||||||
|
{
|
||||||
|
return a.tree == b.tree;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* <Short Description>
|
||||||
|
* Copyright (C) 2024 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <blt/gp/util/statistics.h>
|
||||||
|
#include <blt/format/format.h>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
namespace blt::gp {
|
||||||
|
|
||||||
|
std::string confusion_matrix_t::pretty_print(const std::string& table_name) const
|
||||||
|
{
|
||||||
|
string::TableFormatter formatter{table_name};
|
||||||
|
formatter.addColumn("Predicted " + name_A);
|
||||||
|
formatter.addColumn("Predicted " + name_B);
|
||||||
|
formatter.addColumn("Actual Class");
|
||||||
|
|
||||||
|
string::TableRow row;
|
||||||
|
row.rowValues.push_back(std::to_string(is_A_pred_A));
|
||||||
|
row.rowValues.push_back(std::to_string(is_A_pred_B));
|
||||||
|
row.rowValues.push_back(name_A);
|
||||||
|
formatter.addRow(row);
|
||||||
|
|
||||||
|
string::TableRow row2;
|
||||||
|
row2.rowValues.push_back(std::to_string(is_B_pred_A));
|
||||||
|
row2.rowValues.push_back(std::to_string(is_B_pred_B));
|
||||||
|
row2.rowValues.push_back(name_B);
|
||||||
|
formatter.addRow(row2);
|
||||||
|
|
||||||
|
auto tbl = formatter.createTable(true, true);
|
||||||
|
return std::accumulate(tbl.begin(), tbl.end(), std::string{}, [](const std::string& a, const std::string& b)
|
||||||
|
{
|
||||||
|
return a + "\n" + b;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -15,9 +15,10 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <blt/gp/stats.h>
|
#include <blt/gp/util/trackers.h>
|
||||||
#include <blt/std/logging.h>
|
#include <blt/logging/logging.h>
|
||||||
#include "blt/std/format.h"
|
#include "blt/format/format.h"
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
namespace blt::gp
|
namespace blt::gp
|
||||||
{
|
{
|
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
* <Short Description>
|
||||||
|
* Copyright (C) 2025 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "../examples/symbolic_regression.h"
|
||||||
|
#include <blt/gp/program.h>
|
||||||
|
#include <blt/logging/logging.h>
|
||||||
|
|
||||||
|
using namespace blt::gp;
|
||||||
|
|
||||||
|
std::atomic_uint64_t normal_construct = 0;
|
||||||
|
std::atomic_uint64_t ephemeral_construct = 0;
|
||||||
|
std::atomic_uint64_t normal_drop = 0;
|
||||||
|
std::atomic_uint64_t ephemeral_drop = 0;
|
||||||
|
std::atomic_uint64_t max_allocated = 0;
|
||||||
|
|
||||||
|
struct drop_type
|
||||||
|
{
|
||||||
|
float* m_value;
|
||||||
|
bool ephemeral = false;
|
||||||
|
|
||||||
|
drop_type() : m_value(new float(0))
|
||||||
|
{
|
||||||
|
++normal_construct;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit drop_type(const float silly) : m_value(new float(silly))
|
||||||
|
{
|
||||||
|
++normal_construct;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit drop_type(const float silly, bool) : m_value(new float(silly)), ephemeral(true)
|
||||||
|
{
|
||||||
|
// BLT_TRACE("Constructor with value %f", silly);
|
||||||
|
++ephemeral_construct;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] float value() const
|
||||||
|
{
|
||||||
|
return *m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void drop() const
|
||||||
|
{
|
||||||
|
if (ephemeral)
|
||||||
|
{
|
||||||
|
std::cout << ("Ephemeral drop") << std::endl;
|
||||||
|
++ephemeral_drop;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++normal_drop;
|
||||||
|
delete m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::ostream& operator<<(std::ostream& os, const drop_type& dt)
|
||||||
|
{
|
||||||
|
os << dt.m_value;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct context
|
||||||
|
{
|
||||||
|
float x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
prog_config_t config = prog_config_t()
|
||||||
|
.set_initial_min_tree_size(2)
|
||||||
|
.set_initial_max_tree_size(6)
|
||||||
|
.set_elite_count(2)
|
||||||
|
.set_crossover_chance(0.8)
|
||||||
|
.set_mutation_chance(0.1)
|
||||||
|
.set_reproduction_chance(0.1)
|
||||||
|
.set_max_generations(50)
|
||||||
|
.set_pop_size(500)
|
||||||
|
.set_thread_count(1);
|
||||||
|
|
||||||
|
|
||||||
|
example::symbolic_regression_t regression{691ul, config};
|
||||||
|
|
||||||
|
operation_t add{[](const drop_type a, const drop_type b) { return drop_type{a.value() + b.value()}; }, "add"};
|
||||||
|
operation_t addf{[](const float a, const float b) { return a + b; }, "addf"};
|
||||||
|
operation_t sub([](const drop_type a, const drop_type b) { return drop_type{a.value() - b.value()}; }, "sub");
|
||||||
|
operation_t subf([](const float a, const float b) { return a - b; }, "subf");
|
||||||
|
operation_t mul([](const drop_type a, const drop_type b) { return drop_type{a.value() * b.value()}; }, "mul");
|
||||||
|
operation_t mulf([](const float a, const float b) { return a * b; }, "mulf");
|
||||||
|
operation_t pro_div([](const drop_type a, const drop_type b) { return drop_type{b.value() == 0.0f ? 0.0f : a.value() / b.value()}; }, "div");
|
||||||
|
operation_t pro_divf([](const float a, const float b) { return b == 0.0f ? 0.0f : a / b; }, "divf");
|
||||||
|
operation_t op_sin([](const drop_type a) { return drop_type{std::sin(a.value())}; }, "sin");
|
||||||
|
operation_t op_sinf([](const float a) { return std::sin(a); }, "sinf");
|
||||||
|
operation_t op_cos([](const drop_type a) { return drop_type{std::cos(a.value())}; }, "cos");
|
||||||
|
operation_t op_cosf([](const float a) { return std::cos(a); }, "cosf");
|
||||||
|
operation_t op_exp([](const drop_type a) { return drop_type{std::exp(a.value())}; }, "exp");
|
||||||
|
operation_t op_expf([](const float a) { return std::exp(a); }, "expf");
|
||||||
|
operation_t op_log([](const drop_type a) { return drop_type{a.value() <= 0.0f ? 0.0f : std::log(a.value())}; }, "log");
|
||||||
|
operation_t op_logf([](const float a) { return a <= 0.0f ? 0.0f : std::log(a); }, "logf");
|
||||||
|
operation_t op_tof([](const drop_type a) { return a.value(); }, "to_f");
|
||||||
|
operation_t op_todrop([](const float a) { return drop_type{a}; }, "to_drop");
|
||||||
|
operation_t op_mixed_input([](const drop_type a, const float f)
|
||||||
|
{
|
||||||
|
return a.value() + f;
|
||||||
|
}, "mixed_input");
|
||||||
|
auto lit = operation_t([]()
|
||||||
|
{
|
||||||
|
return drop_type{regression.get_program().get_random().get_float(-1.0f, 1.0f), true};
|
||||||
|
}, "lit").set_ephemeral();
|
||||||
|
|
||||||
|
auto litf = operation_t([]()
|
||||||
|
{
|
||||||
|
return regression.get_program().get_random().get_float(-1.0f, 1.0f);
|
||||||
|
}, "litf").set_ephemeral();
|
||||||
|
|
||||||
|
operation_t op_x([](const context& context)
|
||||||
|
{
|
||||||
|
return drop_type{context.x};
|
||||||
|
}, "x");
|
||||||
|
|
||||||
|
operation_t op_xf([](const context& context)
|
||||||
|
{
|
||||||
|
return context.x;
|
||||||
|
}, "xf");
|
||||||
|
|
||||||
|
bool fitness_function(const tree_t& current_tree, fitness_t& fitness, size_t)
|
||||||
|
{
|
||||||
|
if (normal_construct - normal_drop > max_allocated)
|
||||||
|
max_allocated = normal_construct - normal_drop;
|
||||||
|
constexpr static double value_cutoff = 1.e15;
|
||||||
|
for (auto& fitness_case : regression.get_training_cases())
|
||||||
|
{
|
||||||
|
BLT_GP_UPDATE_CONTEXT(fitness_case);
|
||||||
|
auto val = current_tree.get_evaluation_ref<drop_type>(fitness_case);
|
||||||
|
const auto diff = std::abs(fitness_case.y - val.get().value());
|
||||||
|
if (diff < value_cutoff)
|
||||||
|
{
|
||||||
|
fitness.raw_fitness += diff;
|
||||||
|
if (diff <= 0.01)
|
||||||
|
fitness.hits++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fitness.raw_fitness += value_cutoff;
|
||||||
|
}
|
||||||
|
fitness.standardized_fitness = fitness.raw_fitness;
|
||||||
|
fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness));
|
||||||
|
return static_cast<size_t>(fitness.hits) == regression.get_training_cases().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
operator_builder<context> builder{};
|
||||||
|
builder.build(add, sub, mul, pro_div, op_sin, op_cos, op_exp, op_log, op_mixed_input, lit, op_x, addf, subf, mulf, pro_divf, op_sinf, op_cosf,
|
||||||
|
op_expf, op_logf,
|
||||||
|
litf, op_xf, op_tof, op_todrop);
|
||||||
|
regression.get_program().set_operations(builder.grab());
|
||||||
|
|
||||||
|
auto& program = regression.get_program();
|
||||||
|
static auto sel = select_tournament_t{};
|
||||||
|
program.generate_initial_population(program.get_typesystem().get_type<drop_type>().id());
|
||||||
|
program.setup_generational_evaluation(fitness_function, sel, sel, sel);
|
||||||
|
while (!program.should_terminate())
|
||||||
|
{
|
||||||
|
BLT_TRACE("---------------\\{Begin Generation {}}---------------", program.get_current_generation());
|
||||||
|
BLT_TRACE("Creating next generation");
|
||||||
|
program.create_next_generation();
|
||||||
|
BLT_TRACE("Move to next generation");
|
||||||
|
program.next_generation();
|
||||||
|
BLT_TRACE("Evaluate Fitness");
|
||||||
|
program.evaluate_fitness();
|
||||||
|
}
|
||||||
|
|
||||||
|
// program.get_best_individuals<1>()[0].get().tree.print(program, std::cout, true, true);
|
||||||
|
|
||||||
|
regression.get_program().get_current_pop().clear();
|
||||||
|
regression.get_program().next_generation();
|
||||||
|
regression.get_program().get_current_pop().clear();
|
||||||
|
|
||||||
|
BLT_TRACE("Created {} times", normal_construct.load());
|
||||||
|
BLT_TRACE("Dropped {} times", normal_drop.load());
|
||||||
|
BLT_TRACE("Ephemeral created {} times", ephemeral_construct.load());
|
||||||
|
BLT_TRACE("Ephemeral dropped {} times", ephemeral_drop.load());
|
||||||
|
BLT_TRACE("Max allocated {} times", max_allocated.load());
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
* <Short Description>
|
||||||
|
* Copyright (C) 2025 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "../examples/symbolic_regression.h"
|
||||||
|
#include <blt/gp/program.h>
|
||||||
|
#include <blt/logging/logging.h>
|
||||||
|
|
||||||
|
using namespace blt::gp;
|
||||||
|
|
||||||
|
std::atomic_uint64_t normal_construct = 0;
|
||||||
|
std::atomic_uint64_t ephemeral_construct = 0;
|
||||||
|
std::atomic_uint64_t normal_drop = 0;
|
||||||
|
std::atomic_uint64_t ephemeral_drop = 0;
|
||||||
|
std::atomic_uint64_t max_allocated = 0;
|
||||||
|
|
||||||
|
struct drop_type
|
||||||
|
{
|
||||||
|
float* m_value;
|
||||||
|
bool ephemeral = false;
|
||||||
|
|
||||||
|
drop_type() : m_value(new float(0))
|
||||||
|
{
|
||||||
|
++normal_construct;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit drop_type(const float silly) : m_value(new float(silly))
|
||||||
|
{
|
||||||
|
++normal_construct;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit drop_type(const float silly, bool) : m_value(new float(silly)), ephemeral(true)
|
||||||
|
{
|
||||||
|
// BLT_TRACE("Constructor with value %f", silly);
|
||||||
|
++ephemeral_construct;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] float value() const
|
||||||
|
{
|
||||||
|
return *m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void drop() const
|
||||||
|
{
|
||||||
|
if (ephemeral)
|
||||||
|
{
|
||||||
|
std::cout << ("Ephemeral drop") << std::endl;
|
||||||
|
++ephemeral_drop;
|
||||||
|
}else
|
||||||
|
++normal_drop;
|
||||||
|
delete m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::ostream& operator<<(std::ostream& os, const drop_type& dt)
|
||||||
|
{
|
||||||
|
os << dt.m_value;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct context
|
||||||
|
{
|
||||||
|
float x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
prog_config_t config = prog_config_t()
|
||||||
|
.set_initial_min_tree_size(2)
|
||||||
|
.set_initial_max_tree_size(6)
|
||||||
|
.set_elite_count(2)
|
||||||
|
.set_crossover_chance(0.8)
|
||||||
|
.set_mutation_chance(0.0)
|
||||||
|
.set_reproduction_chance(0.1)
|
||||||
|
.set_max_generations(50)
|
||||||
|
.set_pop_size(500)
|
||||||
|
.set_thread_count(0);
|
||||||
|
|
||||||
|
|
||||||
|
example::symbolic_regression_t regression{691ul, config};
|
||||||
|
|
||||||
|
operation_t add{[](const drop_type a, const drop_type b) { return drop_type{a.value() + b.value()}; }, "add"};
|
||||||
|
operation_t sub([](const drop_type a, const drop_type b) { return drop_type{a.value() - b.value()}; }, "sub");
|
||||||
|
operation_t mul([](const drop_type a, const drop_type b) { return drop_type{a.value() * b.value()}; }, "mul");
|
||||||
|
operation_t pro_div([](const drop_type a, const drop_type b) { return drop_type{b.value() == 0.0f ? 0.0f : a.value() / b.value()}; }, "div");
|
||||||
|
operation_t op_sin([](const drop_type a) { return drop_type{std::sin(a.value())}; }, "sin");
|
||||||
|
operation_t op_cos([](const drop_type a) { return drop_type{std::cos(a.value())}; }, "cos");
|
||||||
|
operation_t op_exp([](const drop_type a) { return drop_type{std::exp(a.value())}; }, "exp");
|
||||||
|
operation_t op_log([](const drop_type a) { return drop_type{a.value() <= 0.0f ? 0.0f : std::log(a.value())}; }, "log");
|
||||||
|
auto lit = operation_t([]()
|
||||||
|
{
|
||||||
|
return drop_type{regression.get_program().get_random().get_float(-1.0f, 1.0f), true};
|
||||||
|
}, "lit").set_ephemeral();
|
||||||
|
|
||||||
|
operation_t op_x([](const context& context)
|
||||||
|
{
|
||||||
|
return drop_type{context.x};
|
||||||
|
}, "x");
|
||||||
|
|
||||||
|
bool fitness_function(const tree_t& current_tree, fitness_t& fitness, size_t)
|
||||||
|
{
|
||||||
|
if (normal_construct - normal_drop > max_allocated)
|
||||||
|
max_allocated = normal_construct - normal_drop;
|
||||||
|
constexpr static double value_cutoff = 1.e15;
|
||||||
|
for (auto& fitness_case : regression.get_training_cases())
|
||||||
|
{
|
||||||
|
BLT_GP_UPDATE_CONTEXT(fitness_case);
|
||||||
|
auto val = current_tree.get_evaluation_ref<drop_type>(fitness_case);
|
||||||
|
const auto diff = std::abs(fitness_case.y - val.get().value());
|
||||||
|
if (diff < value_cutoff)
|
||||||
|
{
|
||||||
|
fitness.raw_fitness += diff;
|
||||||
|
if (diff <= 0.01)
|
||||||
|
fitness.hits++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fitness.raw_fitness += value_cutoff;
|
||||||
|
}
|
||||||
|
fitness.standardized_fitness = fitness.raw_fitness;
|
||||||
|
fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness));
|
||||||
|
return static_cast<size_t>(fitness.hits) == regression.get_training_cases().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
operator_builder<context> builder{};
|
||||||
|
builder.build(add, sub, mul, pro_div, op_sin, op_cos, op_exp, op_log, lit, op_x);
|
||||||
|
regression.get_program().set_operations(builder.grab());
|
||||||
|
|
||||||
|
auto& program = regression.get_program();
|
||||||
|
static auto sel = select_tournament_t{};
|
||||||
|
program.generate_initial_population(program.get_typesystem().get_type<drop_type>().id());
|
||||||
|
program.setup_generational_evaluation(fitness_function, sel, sel, sel);
|
||||||
|
while (!program.should_terminate())
|
||||||
|
{
|
||||||
|
BLT_TRACE("---------------\\{Begin Generation {}}---------------", program.get_current_generation());
|
||||||
|
BLT_TRACE("Creating next generation");
|
||||||
|
program.create_next_generation();
|
||||||
|
BLT_TRACE("Move to next generation");
|
||||||
|
program.next_generation();
|
||||||
|
BLT_TRACE("Evaluate Fitness");
|
||||||
|
program.evaluate_fitness();
|
||||||
|
}
|
||||||
|
|
||||||
|
// program.get_best_individuals<1>()[0].get().tree.print(program, std::cout, true, true);
|
||||||
|
|
||||||
|
regression.get_program().get_current_pop().clear();
|
||||||
|
regression.get_program().next_generation();
|
||||||
|
regression.get_program().get_current_pop().clear();
|
||||||
|
|
||||||
|
BLT_TRACE("Created {} times", normal_construct.load());
|
||||||
|
BLT_TRACE("Dropped {} times", normal_drop.load());
|
||||||
|
BLT_TRACE("Ephemeral created {} times", ephemeral_construct.load());
|
||||||
|
BLT_TRACE("Ephemeral dropped {} times", ephemeral_drop.load());
|
||||||
|
BLT_TRACE("Max allocated {} times", max_allocated.load());
|
||||||
|
|
||||||
|
}
|
|
@ -22,7 +22,7 @@
|
||||||
#include <blt/gp/program.h>
|
#include <blt/gp/program.h>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
const blt::u64 SEED = std::random_device()();
|
static const auto SEED_FUNC = [] { return std::random_device()(); };
|
||||||
|
|
||||||
struct large_256
|
struct large_256
|
||||||
{
|
{
|
||||||
|
@ -52,13 +52,12 @@ struct large_18290
|
||||||
|
|
||||||
large_18290 base{};
|
large_18290 base{};
|
||||||
|
|
||||||
blt::gp::type_provider type_system;
|
blt::gp::gp_program program{SEED_FUNC};
|
||||||
blt::gp::gp_program program{type_system, SEED};
|
|
||||||
|
|
||||||
blt::gp::op_container_t make_container(blt::gp::operator_id id)
|
blt::gp::op_container_t make_container(blt::gp::operator_id id)
|
||||||
{
|
{
|
||||||
auto& info = program.get_operator_info(id);
|
auto& info = program.get_operator_info(id);
|
||||||
return {type_system.get_type(info.return_type).size(), id, false};
|
return {program.get_typesystem().get_type(info.return_type).size(), id, false};
|
||||||
}
|
}
|
||||||
|
|
||||||
blt::gp::op_container_t make_value(const blt::gp::type& id)
|
blt::gp::op_container_t make_value(const blt::gp::type& id)
|
||||||
|
@ -107,12 +106,12 @@ void basic_tree()
|
||||||
blt::gp::tree_t tree{program};
|
blt::gp::tree_t tree{program};
|
||||||
|
|
||||||
tree.get_operations().push_back(make_container(sub.id));
|
tree.get_operations().push_back(make_container(sub.id));
|
||||||
tree.get_operations().push_back(make_value(type_system.get_type<float>()));
|
tree.get_operations().push_back(make_value(program.get_typesystem().get_type<float>()));
|
||||||
tree.get_operations().push_back(make_value(type_system.get_type<float>()));
|
tree.get_operations().push_back(make_value(program.get_typesystem().get_type<float>()));
|
||||||
tree.get_values().push(50.0f);
|
tree.get_values().push(50.0f);
|
||||||
tree.get_values().push(120.0f);
|
tree.get_values().push(120.0f);
|
||||||
|
|
||||||
auto val = tree.get_evaluation_value<float>(nullptr);
|
auto val = tree.get_evaluation_value<float>();
|
||||||
BLT_TRACE(val);
|
BLT_TRACE(val);
|
||||||
BLT_ASSERT(val == (120 - 50));
|
BLT_ASSERT(val == (120 - 50));
|
||||||
}
|
}
|
||||||
|
@ -125,16 +124,16 @@ void large_cross_type_tree()
|
||||||
|
|
||||||
ops.push_back(make_container(cross_large_type.id));
|
ops.push_back(make_container(cross_large_type.id));
|
||||||
ops.push_back(make_container(sub.id));
|
ops.push_back(make_container(sub.id));
|
||||||
ops.push_back(make_value(type_system.get_type<float>()));
|
ops.push_back(make_value(program.get_typesystem().get_type<float>()));
|
||||||
ops.push_back(make_value(type_system.get_type<float>()));
|
ops.push_back(make_value(program.get_typesystem().get_type<float>()));
|
||||||
ops.push_back(make_value(type_system.get_type<float>()));
|
ops.push_back(make_value(program.get_typesystem().get_type<float>()));
|
||||||
ops.push_back(make_container(large_literal.id));
|
ops.push_back(make_container(large_literal.id));
|
||||||
|
|
||||||
vals.push(50.0f);
|
vals.push(50.0f);
|
||||||
vals.push(120.0f);
|
vals.push(120.0f);
|
||||||
vals.push(5555.0f);
|
vals.push(5555.0f);
|
||||||
|
|
||||||
auto val = tree.get_evaluation_value<large_18290>(nullptr);
|
auto val = tree.get_evaluation_value<large_18290>();
|
||||||
blt::black_box(val);
|
blt::black_box(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,11 +142,7 @@ int main()
|
||||||
for (auto& v : base.data)
|
for (auto& v : base.data)
|
||||||
v = static_cast<blt::u8>(blt::random::murmur_random64c(691, std::numeric_limits<blt::u8>::min(), std::numeric_limits<blt::u8>::max()));
|
v = static_cast<blt::u8>(blt::random::murmur_random64c(691, std::numeric_limits<blt::u8>::min(), std::numeric_limits<blt::u8>::max()));
|
||||||
|
|
||||||
type_system.register_type<float>();
|
blt::gp::operator_builder builder{};
|
||||||
type_system.register_type<bool>();
|
|
||||||
type_system.register_type<large_18290>();
|
|
||||||
|
|
||||||
blt::gp::operator_builder builder{type_system};
|
|
||||||
program.set_operations(builder.build(f_literal, b_literal, add, basic_2t, sub, large_literal, cross_large_type));
|
program.set_operations(builder.build(f_literal, b_literal, add, basic_2t, sub, large_literal, cross_large_type));
|
||||||
|
|
||||||
basic_tree();
|
basic_tree();
|
|
@ -438,9 +438,7 @@ int main()
|
||||||
return ctx.x;
|
return ctx.x;
|
||||||
});
|
});
|
||||||
|
|
||||||
blt::gp::type_provider system;
|
blt::gp::operator_builder<context> ops{};
|
||||||
system.register_type<float>();
|
|
||||||
blt::gp::operator_builder<context> ops{system};
|
|
||||||
|
|
||||||
//BLT_TRACE(blt::type_string<decltype(silly_op_3)::first::type>());
|
//BLT_TRACE(blt::type_string<decltype(silly_op_3)::first::type>());
|
||||||
//BLT_TRACE(typeid(decltype(silly_op_3)::first::type).name());
|
//BLT_TRACE(typeid(decltype(silly_op_3)::first::type).name());
|
|
@ -21,10 +21,9 @@
|
||||||
#include <blt/std/logging.h>
|
#include <blt/std/logging.h>
|
||||||
|
|
||||||
|
|
||||||
static constexpr long SEED = 41912;
|
static const auto SEED_FUNC = [] { return std::random_device()(); };
|
||||||
|
|
||||||
blt::gp::type_provider type_system;
|
blt::gp::gp_program program(SEED_FUNC); // NOLINT
|
||||||
blt::gp::gp_program program(type_system, SEED); // NOLINT
|
|
||||||
|
|
||||||
blt::gp::operation_t add([](float a, float b) {
|
blt::gp::operation_t add([](float a, float b) {
|
||||||
BLT_TRACE("a: %f + b: %f = %f", a, b, a + b);
|
BLT_TRACE("a: %f + b: %f = %f", a, b, a + b);
|
||||||
|
@ -53,16 +52,15 @@ auto lit = blt::gp::operation_t([]() {
|
||||||
*/
|
*/
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
type_system.register_type<float>();
|
blt::gp::operator_builder silly{};
|
||||||
|
|
||||||
blt::gp::operator_builder silly{type_system};
|
|
||||||
|
|
||||||
program.set_operations(silly.build(add, sub, mul, pro_div, lit));
|
program.set_operations(silly.build(add, sub, mul, pro_div, lit));
|
||||||
|
|
||||||
blt::gp::grow_generator_t grow;
|
blt::gp::grow_generator_t grow;
|
||||||
auto tree = grow.generate(blt::gp::generator_arguments{program, type_system.get_type<float>().id(), 3, 7});
|
blt::gp::tree_t tree{program};
|
||||||
|
grow.generate(tree, blt::gp::generator_arguments{program, program.get_typesystem().get_type<float>().id(), 3, 7});
|
||||||
|
|
||||||
auto value = tree.get_evaluation_value<float>(nullptr);
|
auto value = tree.get_evaluation_value<float>();
|
||||||
|
|
||||||
BLT_TRACE(value);
|
BLT_TRACE(value);
|
||||||
|
|
|
@ -20,10 +20,9 @@
|
||||||
#include <blt/gp/tree.h>
|
#include <blt/gp/tree.h>
|
||||||
#include <blt/std/logging.h>
|
#include <blt/std/logging.h>
|
||||||
|
|
||||||
static constexpr long SEED = 41912;
|
static const auto SEED_FUNC = [] { return std::random_device()(); };
|
||||||
|
|
||||||
blt::gp::type_provider type_system;
|
blt::gp::gp_program program(SEED_FUNC); // NOLINT
|
||||||
blt::gp::gp_program program(type_system, SEED); // NOLINT
|
|
||||||
|
|
||||||
blt::gp::operation_t add([](float a, float b) { return a + b; });
|
blt::gp::operation_t add([](float a, float b) { return a + b; });
|
||||||
blt::gp::operation_t sub([](float a, float b) { return a - b; });
|
blt::gp::operation_t sub([](float a, float b) { return a - b; });
|
||||||
|
@ -51,16 +50,14 @@ auto lit = blt::gp::operation_t([]() {
|
||||||
*/
|
*/
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
type_system.register_type<float>();
|
blt::gp::operator_builder silly{};
|
||||||
type_system.register_type<bool>();
|
|
||||||
|
|
||||||
blt::gp::operator_builder silly{type_system};
|
|
||||||
program.set_operations(silly.build(add, sub, mul, pro_div, op_if, eq_f, eq_b, lt, gt, op_and, op_or, op_xor, op_not, lit));
|
program.set_operations(silly.build(add, sub, mul, pro_div, op_if, eq_f, eq_b, lt, gt, op_and, op_or, op_xor, op_not, lit));
|
||||||
|
|
||||||
blt::gp::grow_generator_t grow;
|
blt::gp::grow_generator_t grow;
|
||||||
auto tree = grow.generate(blt::gp::generator_arguments{program, type_system.get_type<float>().id(), 3, 7});
|
blt::gp::tree_t tree{program};
|
||||||
|
grow.generate(tree, blt::gp::generator_arguments{program, program.get_typesystem().get_type<float>().id(), 3, 7});
|
||||||
|
|
||||||
auto value = tree.get_evaluation_value<float>(nullptr);
|
auto value = tree.get_evaluation_value<float>();
|
||||||
|
|
||||||
BLT_TRACE(value);
|
BLT_TRACE(value);
|
||||||
|
|
|
@ -20,10 +20,9 @@
|
||||||
#include <blt/gp/tree.h>
|
#include <blt/gp/tree.h>
|
||||||
#include <blt/std/logging.h>
|
#include <blt/std/logging.h>
|
||||||
|
|
||||||
static constexpr long SEED = 41912;
|
static const auto SEED_FUNC = [] { return std::random_device()(); };
|
||||||
|
|
||||||
blt::gp::type_provider type_system;
|
blt::gp::gp_program program(SEED_FUNC); // NOLINT
|
||||||
blt::gp::gp_program program(type_system, SEED); // NOLINT
|
|
||||||
|
|
||||||
blt::gp::operation_t add([](float a, float b) { return a + b; });
|
blt::gp::operation_t add([](float a, float b) { return a + b; });
|
||||||
blt::gp::operation_t sub([](float a, float b) { return a - b; });
|
blt::gp::operation_t sub([](float a, float b) { return a - b; });
|
||||||
|
@ -51,19 +50,16 @@ auto lit = blt::gp::operation_t([]() {
|
||||||
*/
|
*/
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
type_system.register_type<float>();
|
blt::gp::operator_builder builder{};
|
||||||
type_system.register_type<bool>();
|
|
||||||
|
|
||||||
blt::gp::operator_builder builder{type_system};
|
|
||||||
program.set_operations(builder.build(add, sub, mul, pro_div, op_if, eq_f, eq_b, lt, gt, op_and, op_or, op_xor, op_not, lit));
|
program.set_operations(builder.build(add, sub, mul, pro_div, op_if, eq_f, eq_b, lt, gt, op_and, op_or, op_xor, op_not, lit));
|
||||||
|
|
||||||
blt::gp::ramped_half_initializer_t pop_init;
|
blt::gp::ramped_half_initializer_t pop_init;
|
||||||
|
|
||||||
auto pop = pop_init.generate(blt::gp::initializer_arguments{program, type_system.get_type<float>().id(), 500, 3, 10});
|
auto pop = pop_init.generate(blt::gp::initializer_arguments{program, program.get_typesystem().get_type<float>().id(), 500, 3, 10});
|
||||||
|
|
||||||
for (auto& tree : pop.for_each_tree())
|
for (auto& tree : pop.for_each_tree())
|
||||||
{
|
{
|
||||||
auto value = tree.get_evaluation_value<float>(nullptr);
|
auto value = tree.get_evaluation_value<float>();
|
||||||
|
|
||||||
BLT_TRACE(value);
|
BLT_TRACE(value);
|
||||||
}
|
}
|
|
@ -40,11 +40,9 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
static constexpr long SEED = 41912;
|
static const auto SEED_FUNC = [] { return std::random_device()(); };
|
||||||
|
|
||||||
|
blt::gp::gp_program program(SEED_FUNC); // NOLINT
|
||||||
blt::gp::type_provider type_system;
|
|
||||||
blt::gp::gp_program program(type_system, SEED); // NOLINT
|
|
||||||
|
|
||||||
blt::gp::operation_t add([](float a, float b) { return a + b; }, "add"); // 0
|
blt::gp::operation_t add([](float a, float b) { return a + b; }, "add"); // 0
|
||||||
blt::gp::operation_t sub([](float a, float b) { return a - b; }, "sub"); // 1
|
blt::gp::operation_t sub([](float a, float b) { return a - b; }, "sub"); // 1
|
||||||
|
@ -72,15 +70,12 @@ auto lit = blt::gp::operation_t([]() {
|
||||||
*/
|
*/
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
type_system.register_type<float>();
|
blt::gp::operator_builder builder{};
|
||||||
type_system.register_type<bool>();
|
|
||||||
|
|
||||||
blt::gp::operator_builder builder{type_system};
|
|
||||||
program.set_operations(builder.build(add, sub, mul, pro_div, op_if, eq_f, eq_b, lt, gt, op_and, op_or, op_xor, op_not, lit));
|
program.set_operations(builder.build(add, sub, mul, pro_div, op_if, eq_f, eq_b, lt, gt, op_and, op_or, op_xor, op_not, lit));
|
||||||
|
|
||||||
blt::gp::ramped_half_initializer_t pop_init;
|
blt::gp::ramped_half_initializer_t pop_init;
|
||||||
|
|
||||||
auto pop = pop_init.generate(blt::gp::initializer_arguments{program, type_system.get_type<float>().id(), 500, 3, 10});
|
auto pop = pop_init.generate(blt::gp::initializer_arguments{program, program.get_typesystem().get_type<float>().id(), 500, 3, 10});
|
||||||
|
|
||||||
// for (auto& tree : pop.getIndividuals())
|
// for (auto& tree : pop.getIndividuals())
|
||||||
// {
|
// {
|
||||||
|
@ -99,7 +94,7 @@ int main()
|
||||||
BLT_INFO("Pre-Crossover:");
|
BLT_INFO("Pre-Crossover:");
|
||||||
for (auto& tree : pop.get_individuals())
|
for (auto& tree : pop.get_individuals())
|
||||||
{
|
{
|
||||||
auto f = tree.tree.get_evaluation_value<float>(nullptr);
|
auto f = tree.tree.get_evaluation_value<float>();
|
||||||
pre.push_back(f);
|
pre.push_back(f);
|
||||||
BLT_TRACE(f);
|
BLT_TRACE(f);
|
||||||
}
|
}
|
||||||
|
@ -116,34 +111,31 @@ int main()
|
||||||
second = random.get_size_t(0ul, pop.get_individuals().size());
|
second = random.get_size_t(0ul, pop.get_individuals().size());
|
||||||
} while (second == first);
|
} while (second == first);
|
||||||
|
|
||||||
auto results = crossover.apply(program, ind[first].tree, ind[second].tree);
|
blt::gp::tree_t child1{program};
|
||||||
if (results.has_value())
|
blt::gp::tree_t child2{program};
|
||||||
|
// crossover function assumes that children have been copied from parents
|
||||||
|
child1.copy_fast(ind[first].tree);
|
||||||
|
child2.copy_fast(ind[second].tree);
|
||||||
|
auto results = crossover.apply(program, ind[first].tree, ind[second].tree, child1, child2);
|
||||||
|
if (results)
|
||||||
{
|
{
|
||||||
// bool print_literals = true;
|
// bool print_literals = true;
|
||||||
// bool pretty_print = false;
|
// bool pretty_print = false;
|
||||||
// bool print_returns = false;
|
// bool print_returns = false;
|
||||||
// BLT_TRACE("Parent 1: %f", ind[0].get_evaluation_value<float>(nullptr));
|
// BLT_TRACE("Parent 1: %f", ind[0].get_evaluation_value<float>());
|
||||||
// ind[0].print(program, std::cout, print_literals, pretty_print, print_returns);
|
// ind[0].print(program, std::cout, print_literals, pretty_print, print_returns);
|
||||||
// BLT_TRACE("Parent 2: %f", ind[1].get_evaluation_value<float>(nullptr));
|
// BLT_TRACE("Parent 2: %f", ind[1].get_evaluation_value<float>());
|
||||||
// ind[1].print(program, std::cout, print_literals, pretty_print, print_returns);
|
// ind[1].print(program, std::cout, print_literals, pretty_print, print_returns);
|
||||||
// BLT_TRACE("------------");
|
// BLT_TRACE("------------");
|
||||||
// BLT_TRACE("Child 1: %f", results->child1.get_evaluation_value<float>(nullptr));
|
// BLT_TRACE("Child 1: %f", results->child1.get_evaluation_value<float>());
|
||||||
// results->child1.print(program, std::cout, print_literals, pretty_print, print_returns);
|
// results->child1.print(program, std::cout, print_literals, pretty_print, print_returns);
|
||||||
// BLT_TRACE("Child 2: %f", results->child2.get_evaluation_value<float>(nullptr));
|
// BLT_TRACE("Child 2: %f", results->child2.get_evaluation_value<float>());
|
||||||
// results->child2.print(program, std::cout, print_literals, pretty_print, print_returns);
|
// results->child2.print(program, std::cout, print_literals, pretty_print, print_returns);
|
||||||
new_pop.get_individuals().emplace_back(std::move(results->child1));
|
new_pop.get_individuals().emplace_back(std::move(child1));
|
||||||
new_pop.get_individuals().emplace_back(std::move(results->child2));
|
new_pop.get_individuals().emplace_back(std::move(child2));
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
switch (results.error())
|
BLT_DEBUG("Crossover Failed.");
|
||||||
{
|
|
||||||
case blt::gp::crossover_t::error_t::NO_VALID_TYPE:
|
|
||||||
BLT_DEBUG("No valid type!");
|
|
||||||
break;
|
|
||||||
case blt::gp::crossover_t::error_t::TREE_TOO_SMALL:
|
|
||||||
BLT_DEBUG("Tree is too small!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
errors++;
|
errors++;
|
||||||
new_pop.get_individuals().push_back(ind[first]);
|
new_pop.get_individuals().push_back(ind[first]);
|
||||||
new_pop.get_individuals().push_back(ind[second]);
|
new_pop.get_individuals().push_back(ind[second]);
|
||||||
|
@ -153,7 +145,7 @@ int main()
|
||||||
BLT_INFO("Post-Crossover:");
|
BLT_INFO("Post-Crossover:");
|
||||||
for (auto& tree : new_pop.for_each_tree())
|
for (auto& tree : new_pop.for_each_tree())
|
||||||
{
|
{
|
||||||
auto f = tree.get_evaluation_value<float>(nullptr);
|
auto f = tree.get_evaluation_value<float>();
|
||||||
pos.push_back(f);
|
pos.push_back(f);
|
||||||
BLT_TRACE(f);
|
BLT_TRACE(f);
|
||||||
}
|
}
|
|
@ -38,11 +38,9 @@
|
||||||
#include <blt/std/logging.h>
|
#include <blt/std/logging.h>
|
||||||
#include <blt/gp/transformers.h>
|
#include <blt/gp/transformers.h>
|
||||||
|
|
||||||
static constexpr long SEED = 41912;
|
static const auto SEED_FUNC = [] { return std::random_device()(); };
|
||||||
|
|
||||||
|
blt::gp::gp_program program(SEED_FUNC); // NOLINT
|
||||||
blt::gp::type_provider type_system;
|
|
||||||
blt::gp::gp_program program(type_system, SEED); // NOLINT
|
|
||||||
|
|
||||||
blt::gp::operation_t add([](float a, float b) { return a + b; }, "add"); // 0
|
blt::gp::operation_t add([](float a, float b) { return a + b; }, "add"); // 0
|
||||||
blt::gp::operation_t sub([](float a, float b) { return a - b; }, "sub"); // 1
|
blt::gp::operation_t sub([](float a, float b) { return a - b; }, "sub"); // 1
|
||||||
|
@ -70,15 +68,12 @@ auto lit = blt::gp::operation_t([]() {
|
||||||
*/
|
*/
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
type_system.register_type<float>();
|
blt::gp::operator_builder builder{};
|
||||||
type_system.register_type<bool>();
|
|
||||||
|
|
||||||
blt::gp::operator_builder builder{type_system};
|
|
||||||
program.set_operations(builder.build(add, sub, mul, pro_div, op_if, eq_f, eq_b, lt, gt, op_and, op_or, op_xor, op_not, lit));
|
program.set_operations(builder.build(add, sub, mul, pro_div, op_if, eq_f, eq_b, lt, gt, op_and, op_or, op_xor, op_not, lit));
|
||||||
|
|
||||||
blt::gp::ramped_half_initializer_t pop_init;
|
blt::gp::ramped_half_initializer_t pop_init;
|
||||||
|
|
||||||
auto pop = pop_init.generate(blt::gp::initializer_arguments{program, type_system.get_type<float>().id(), 500, 3, 10});
|
auto pop = pop_init.generate(blt::gp::initializer_arguments{program, program.get_typesystem().get_type<float>().id(), 500, 3, 10});
|
||||||
|
|
||||||
blt::gp::population_t new_pop;
|
blt::gp::population_t new_pop;
|
||||||
blt::gp::mutation_t mutator;
|
blt::gp::mutation_t mutator;
|
||||||
|
@ -90,19 +85,22 @@ int main()
|
||||||
BLT_INFO("Pre-Mutation:");
|
BLT_INFO("Pre-Mutation:");
|
||||||
for (auto& tree : pop.for_each_tree())
|
for (auto& tree : pop.for_each_tree())
|
||||||
{
|
{
|
||||||
auto f = tree.get_evaluation_value<float>(nullptr);
|
auto f = tree.get_evaluation_value<float>();
|
||||||
pre.push_back(f);
|
pre.push_back(f);
|
||||||
BLT_TRACE(f);
|
BLT_TRACE(f);
|
||||||
}
|
}
|
||||||
BLT_INFO("Mutation:");
|
BLT_INFO("Mutation:");
|
||||||
for (auto& tree : pop.for_each_tree())
|
for (auto& tree : pop.for_each_tree())
|
||||||
{
|
{
|
||||||
new_pop.get_individuals().emplace_back(mutator.apply(program, tree));
|
blt::gp::tree_t tree_out{program};
|
||||||
|
tree_out.copy_fast(tree);
|
||||||
|
mutator.apply(program, tree, tree_out);
|
||||||
|
new_pop.get_individuals().emplace_back(std::move(tree_out));
|
||||||
}
|
}
|
||||||
BLT_INFO("Post-Mutation");
|
BLT_INFO("Post-Mutation");
|
||||||
for (auto& tree : new_pop.for_each_tree())
|
for (auto& tree : new_pop.for_each_tree())
|
||||||
{
|
{
|
||||||
auto f = tree.get_evaluation_value<float>(nullptr);
|
auto f = tree.get_evaluation_value<float>();
|
||||||
pos.push_back(f);
|
pos.push_back(f);
|
||||||
BLT_TRACE(f);
|
BLT_TRACE(f);
|
||||||
}
|
}
|
|
@ -19,12 +19,11 @@
|
||||||
#include <blt/gp/tree.h>
|
#include <blt/gp/tree.h>
|
||||||
#include <blt/std/logging.h>
|
#include <blt/std/logging.h>
|
||||||
|
|
||||||
static constexpr long SEED = 41912;
|
static const auto SEED_FUNC = [] { return std::random_device()(); };
|
||||||
|
|
||||||
blt::gp::prog_config_t config = blt::gp::prog_config_t().set_elite_count(2);
|
blt::gp::prog_config_t config = blt::gp::prog_config_t().set_elite_count(2);
|
||||||
|
|
||||||
blt::gp::type_provider type_system;
|
blt::gp::gp_program program(SEED_FUNC, config); // NOLINT
|
||||||
blt::gp::gp_program program(type_system, SEED, config); // NOLINT
|
|
||||||
std::array<float, 500> result_container;
|
std::array<float, 500> result_container;
|
||||||
|
|
||||||
blt::gp::operation_t add([](float a, float b) { return a + b; }, "add"); // 0
|
blt::gp::operation_t add([](float a, float b) { return a + b; }, "add"); // 0
|
||||||
|
@ -59,7 +58,7 @@ void print_best()
|
||||||
auto& tree = v.tree;
|
auto& tree = v.tree;
|
||||||
auto size = tree.get_values().size();
|
auto size = tree.get_values().size();
|
||||||
BLT_TRACE("%lf [index %ld] (fitness: %lf, raw: %lf) (depth: %ld) (size: t: %ld u: %ld r: %ld) filled: %f%%",
|
BLT_TRACE("%lf [index %ld] (fitness: %lf, raw: %lf) (depth: %ld) (size: t: %ld u: %ld r: %ld) filled: %f%%",
|
||||||
tree.get_evaluation_value<float>(nullptr), i, v.fitness.adjusted_fitness, v.fitness.raw_fitness,
|
tree.get_evaluation_value<float>(), i, v.fitness.adjusted_fitness, v.fitness.raw_fitness,
|
||||||
tree.get_depth(program), size.total_size_bytes, size.total_used_bytes,
|
tree.get_depth(program), size.total_size_bytes, size.total_used_bytes,
|
||||||
size.total_remaining_bytes,
|
size.total_remaining_bytes,
|
||||||
static_cast<double>(size.total_used_bytes) / (size.total_size_bytes == 0 ? 1 : static_cast<double>(size.total_size_bytes)));
|
static_cast<double>(size.total_used_bytes) / (size.total_size_bytes == 0 ? 1 : static_cast<double>(size.total_size_bytes)));
|
||||||
|
@ -75,7 +74,7 @@ constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fit
|
||||||
BLT_DEBUG("(depth: %ld) (blocks: %ld) (size: t: %ld m: %ld u: %ld r: %ld) filled: %f%%",
|
BLT_DEBUG("(depth: %ld) (blocks: %ld) (size: t: %ld m: %ld u: %ld r: %ld) filled: %f%%",
|
||||||
current_tree.get_depth(program), size.blocks, size.total_size_bytes, size.total_no_meta_bytes, size.total_used_bytes,
|
current_tree.get_depth(program), size.blocks, size.total_size_bytes, size.total_no_meta_bytes, size.total_used_bytes,
|
||||||
size.total_remaining_bytes, static_cast<double>(size.total_used_bytes) / static_cast<double>(size.total_no_meta_bytes));*/
|
size.total_remaining_bytes, static_cast<double>(size.total_used_bytes) / static_cast<double>(size.total_no_meta_bytes));*/
|
||||||
result_container[index] = current_tree.get_evaluation_value<float>(nullptr);
|
result_container[index] = current_tree.get_evaluation_value<float>();
|
||||||
fitness.raw_fitness = result_container[index] / 1000000000.0;
|
fitness.raw_fitness = result_container[index] / 1000000000.0;
|
||||||
fitness.standardized_fitness = fitness.raw_fitness;
|
fitness.standardized_fitness = fitness.raw_fitness;
|
||||||
fitness.adjusted_fitness = 1.0 - (1.0 / (1.0 + fitness.raw_fitness));
|
fitness.adjusted_fitness = 1.0 - (1.0 / (1.0 + fitness.raw_fitness));
|
||||||
|
@ -86,14 +85,11 @@ constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fit
|
||||||
*/
|
*/
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
type_system.register_type<float>();
|
blt::gp::operator_builder builder{};
|
||||||
type_system.register_type<bool>();
|
|
||||||
|
|
||||||
blt::gp::operator_builder builder{type_system};
|
|
||||||
program.set_operations(builder.build(add, sub, mul, pro_div, op_if, eq_f, eq_b, lt, gt, op_and, op_or, op_xor, op_not, lit));
|
program.set_operations(builder.build(add, sub, mul, pro_div, op_if, eq_f, eq_b, lt, gt, op_and, op_or, op_xor, op_not, lit));
|
||||||
|
|
||||||
auto sel = blt::gp::select_tournament_t{};
|
auto sel = blt::gp::select_tournament_t{};
|
||||||
program.generate_population(type_system.get_type<float>().id(), fitness_function, sel, sel, sel);
|
program.generate_population(program.get_typesystem().get_type<float>().id(), fitness_function, sel, sel, sel);
|
||||||
|
|
||||||
while (!program.should_terminate())
|
while (!program.should_terminate())
|
||||||
{
|
{
|
|
@ -27,7 +27,6 @@
|
||||||
const blt::u64 SEED = std::random_device()();
|
const blt::u64 SEED = std::random_device()();
|
||||||
//const blt::u64 SEED = 3495535167;
|
//const blt::u64 SEED = 3495535167;
|
||||||
blt::gp::random_t b_rand {SEED};
|
blt::gp::random_t b_rand {SEED};
|
||||||
blt::gp::type_provider type_system;
|
|
||||||
|
|
||||||
struct context
|
struct context
|
||||||
{
|
{
|
||||||
|
@ -82,18 +81,19 @@ auto basic_lit_b = blt::gp::operation_t([]() {
|
||||||
|
|
||||||
void basic_test()
|
void basic_test()
|
||||||
{
|
{
|
||||||
blt::gp::gp_program program{type_system, SEED};
|
blt::gp::gp_program program{SEED};
|
||||||
|
|
||||||
blt::gp::operator_builder<context> builder{type_system};
|
blt::gp::operator_builder<context> builder{};
|
||||||
|
|
||||||
program.set_operations(builder.build(basic_sub, basic_lit_f, basic_lit_b));
|
program.set_operations(builder.build(basic_sub, basic_lit_f, basic_lit_b));
|
||||||
|
|
||||||
blt::gp::grow_generator_t gen;
|
blt::gp::grow_generator_t gen;
|
||||||
blt::gp::generator_arguments args{program, type_system.get_type<float>().id(), 1, 1};
|
blt::gp::generator_arguments args{program, program.get_typesystem().get_type<float>().id(), 1, 1};
|
||||||
auto tree = gen.generate(args);
|
blt::gp::tree_t tree{program};
|
||||||
|
gen.generate(tree, args);
|
||||||
|
|
||||||
context ctx{&program};
|
context ctx{&program};
|
||||||
auto result = tree.get_evaluation_value<float>(&ctx);
|
auto result = tree.get_evaluation_value<float>(ctx);
|
||||||
BLT_TRACE(result);
|
BLT_TRACE(result);
|
||||||
BLT_ASSERT(result == -5.0f || result == 5.0f || result == 0.0f);
|
BLT_ASSERT(result == -5.0f || result == 5.0f || result == 0.0f);
|
||||||
tree.print(program, std::cout, true, true);
|
tree.print(program, std::cout, true, true);
|
||||||
|
@ -103,9 +103,5 @@ int main()
|
||||||
{
|
{
|
||||||
BLT_INFO("Starting with seed %ld", SEED);
|
BLT_INFO("Starting with seed %ld", SEED);
|
||||||
|
|
||||||
type_system.register_type<float>();
|
|
||||||
type_system.register_type<bool>();
|
|
||||||
type_system.register_type<large_18290>();
|
|
||||||
|
|
||||||
basic_test();
|
basic_test();
|
||||||
}
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
* <Short Description>
|
||||||
|
* Copyright (C) 2025 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include "../examples/symbolic_regression.h"
|
||||||
|
#include <blt/gp/program.h>
|
||||||
|
#include <blt/logging/logging.h>
|
||||||
|
#include <ostream>
|
||||||
|
#include <istream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <blt/fs/stream_wrappers.h>
|
||||||
|
|
||||||
|
using namespace blt::gp;
|
||||||
|
|
||||||
|
struct context
|
||||||
|
{
|
||||||
|
float x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
prog_config_t config = prog_config_t()
|
||||||
|
.set_initial_min_tree_size(2)
|
||||||
|
.set_initial_max_tree_size(6)
|
||||||
|
.set_elite_count(2)
|
||||||
|
.set_crossover_chance(0.8)
|
||||||
|
.set_mutation_chance(0.1)
|
||||||
|
.set_reproduction_chance(0.1)
|
||||||
|
.set_max_generations(50)
|
||||||
|
.set_pop_size(500)
|
||||||
|
.set_thread_count(1);
|
||||||
|
|
||||||
|
|
||||||
|
example::symbolic_regression_t regression{691ul, config};
|
||||||
|
|
||||||
|
operation_t addf{[](const float a, const float b) { return a + b; }, "addf"};
|
||||||
|
operation_t subf([](const float a, const float b) { return a - b; }, "subf");
|
||||||
|
operation_t mulf([](const float a, const float b) { return a * b; }, "mulf");
|
||||||
|
operation_t pro_divf([](const float a, const float b) { return b == 0.0f ? 0.0f : a / b; }, "divf");
|
||||||
|
operation_t op_sinf([](const float a) { return std::sin(a); }, "sinf");
|
||||||
|
operation_t op_cosf([](const float a) { return std::cos(a); }, "cosf");
|
||||||
|
operation_t op_expf([](const float a) { return std::exp(a); }, "expf");
|
||||||
|
operation_t op_logf([](const float a) { return a <= 0.0f ? 0.0f : std::log(a); }, "logf");
|
||||||
|
|
||||||
|
auto litf = operation_t([]()
|
||||||
|
{
|
||||||
|
return regression.get_program().get_random().get_float(-1.0f, 1.0f);
|
||||||
|
}, "litf").set_ephemeral();
|
||||||
|
|
||||||
|
operation_t op_xf([](const context& context)
|
||||||
|
{
|
||||||
|
return context.x;
|
||||||
|
}, "xf");
|
||||||
|
|
||||||
|
bool fitness_function(const tree_t& current_tree, fitness_t& fitness, size_t)
|
||||||
|
{
|
||||||
|
constexpr static double value_cutoff = 1.e15;
|
||||||
|
for (auto& fitness_case : regression.get_training_cases())
|
||||||
|
{
|
||||||
|
BLT_GP_UPDATE_CONTEXT(fitness_case);
|
||||||
|
auto val = current_tree.get_evaluation_ref<float>(fitness_case);
|
||||||
|
const auto diff = std::abs(fitness_case.y - val.get());
|
||||||
|
if (diff < value_cutoff)
|
||||||
|
{
|
||||||
|
fitness.raw_fitness += diff;
|
||||||
|
if (diff <= 0.01)
|
||||||
|
fitness.hits++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fitness.raw_fitness += value_cutoff;
|
||||||
|
}
|
||||||
|
fitness.standardized_fitness = fitness.raw_fitness;
|
||||||
|
fitness.adjusted_fitness = (1.0 / (1.0 + fitness.standardized_fitness));
|
||||||
|
return static_cast<size_t>(fitness.hits) == regression.get_training_cases().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
operator_builder<context> builder{};
|
||||||
|
const auto& operators = builder.build(addf, subf, mulf, pro_divf, op_sinf, op_cosf, op_expf, op_logf, litf, op_xf);
|
||||||
|
regression.get_program().set_operations(operators);
|
||||||
|
|
||||||
|
auto& program = regression.get_program();
|
||||||
|
static auto sel = select_tournament_t{};
|
||||||
|
|
||||||
|
gp_program test_program{691};
|
||||||
|
test_program.set_operations(operators);
|
||||||
|
test_program.setup_generational_evaluation(fitness_function, sel, sel, sel, false);
|
||||||
|
|
||||||
|
// simulate a program which is similar but incompatible with the other programs.
|
||||||
|
operator_builder<context> builder2{};
|
||||||
|
gp_program bad_program{691};
|
||||||
|
bad_program.set_operations(builder2.build(addf, subf, mulf, op_sinf, op_cosf, litf, op_xf));
|
||||||
|
bad_program.setup_generational_evaluation(fitness_function, sel, sel, sel, false);
|
||||||
|
|
||||||
|
program.generate_initial_population(program.get_typesystem().get_type<float>().id());
|
||||||
|
program.setup_generational_evaluation(fitness_function, sel, sel, sel);
|
||||||
|
while (!program.should_terminate())
|
||||||
|
{
|
||||||
|
BLT_TRACE("---------------\\{Begin Generation {}}---------------", program.get_current_generation());
|
||||||
|
BLT_TRACE("Creating next generation");
|
||||||
|
program.create_next_generation();
|
||||||
|
BLT_TRACE("Move to next generation");
|
||||||
|
program.next_generation();
|
||||||
|
BLT_TRACE("Evaluate Fitness");
|
||||||
|
program.evaluate_fitness();
|
||||||
|
{
|
||||||
|
std::ofstream stream{"serialization_test.data", std::ios::binary | std::ios::trunc};
|
||||||
|
blt::fs::fstream_writer_t writer{stream};
|
||||||
|
program.save_generation(writer);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::ifstream stream{"serialization_test.data", std::ios::binary};
|
||||||
|
blt::fs::fstream_reader_t reader{stream};
|
||||||
|
test_program.load_generation(reader);
|
||||||
|
}
|
||||||
|
// do a quick validation check
|
||||||
|
for (const auto& [saved, loaded] : blt::zip(program.get_current_pop(), test_program.get_current_pop()))
|
||||||
|
{
|
||||||
|
if (saved.tree != loaded.tree)
|
||||||
|
{
|
||||||
|
BLT_ERROR("Serializer Failed to correctly serialize tree to disk, trees are not equal!");
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::ofstream stream{"serialization_test2.data", std::ios::binary | std::ios::trunc};
|
||||||
|
blt::fs::fstream_writer_t writer{stream};
|
||||||
|
program.save_state(writer);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::ifstream stream{"serialization_test2.data", std::ios::binary};
|
||||||
|
blt::fs::fstream_reader_t reader{stream};
|
||||||
|
if (auto error = test_program.load_state(reader))
|
||||||
|
{
|
||||||
|
BLT_ERROR("Error: {}", error->call_member(&errors::serialization::error_to_string_t::to_string));
|
||||||
|
BLT_ABORT("Expected program to succeeded without returning an error state!");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto [saved, loaded] : blt::zip(program.get_stats_histories(), test_program.get_stats_histories()))
|
||||||
|
{
|
||||||
|
if (saved != loaded)
|
||||||
|
{
|
||||||
|
BLT_ERROR("Serializer Failed to correctly serialize histories to disk, histories are not equal!");
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::ifstream stream{"serialization_test2.data", std::ios::binary};
|
||||||
|
blt::fs::fstream_reader_t reader{stream};
|
||||||
|
if (!bad_program.load_state(reader))
|
||||||
|
{
|
||||||
|
BLT_ABORT("Expected program to throw an exception when parsing state data into an incompatible program!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,203 @@
|
||||||
|
/*
|
||||||
|
* <Short Description>
|
||||||
|
* Copyright (C) 2025 Brett Terpstra
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "../examples/symbolic_regression.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <array>
|
||||||
|
#include <sstream>
|
||||||
|
#include <blt/profiling/profiler_v2.h>
|
||||||
|
|
||||||
|
static const auto SEED_FUNC = [] { return std::random_device()(); };
|
||||||
|
|
||||||
|
std::array crossover_chances = {0.8, 0.9, 1.0};
|
||||||
|
std::array mutation_chances = {0.0, 0.1, 0.2, 0.9, 1.0};
|
||||||
|
std::array reproduction_chances = {0.0, 0.1, 0.9, 1.0};
|
||||||
|
std::array elite_amounts = {0, 2, 50};
|
||||||
|
std::array population_sizes = {50, 500, 5000};
|
||||||
|
|
||||||
|
blt::gp::prog_config_t best_config;
|
||||||
|
double best_fitness = 0;
|
||||||
|
|
||||||
|
void run(const blt::gp::prog_config_t& config)
|
||||||
|
{
|
||||||
|
// the config is copied into the gp_system so changing the config will not change the runtime of the program.
|
||||||
|
blt::gp::example::symbolic_regression_t regression{SEED_FUNC, config};
|
||||||
|
|
||||||
|
|
||||||
|
BLT_START_INTERVAL("Symbolic Regression", "Setup Operations");
|
||||||
|
regression.setup_operations();
|
||||||
|
BLT_END_INTERVAL("Symbolic Regression", "Setup Operations");
|
||||||
|
|
||||||
|
BLT_START_INTERVAL("Symbolic Regression", "Generate Initial Population");
|
||||||
|
regression.generate_initial_population();
|
||||||
|
BLT_END_INTERVAL("Symbolic Regression", "Generate Initial Population");
|
||||||
|
|
||||||
|
BLT_START_INTERVAL("Symbolic Regression", "Total Generation Loop");
|
||||||
|
BLT_DEBUG("Begin Generation Loop");
|
||||||
|
auto& program = regression.get_program();
|
||||||
|
while (!program.should_terminate())
|
||||||
|
{
|
||||||
|
#ifdef BLT_TRACK_ALLOCATIONS
|
||||||
|
auto cross = crossover_calls.start_measurement();
|
||||||
|
auto mut = mutation_calls.start_measurement();
|
||||||
|
auto repo = reproduction_calls.start_measurement();
|
||||||
|
#endif
|
||||||
|
BLT_TRACE("------------\\{Begin Generation {}}------------", program.get_current_generation());
|
||||||
|
BLT_TRACE("Creating next generation");
|
||||||
|
BLT_START_INTERVAL("Symbolic Regression", "Create Next Generation");
|
||||||
|
program.create_next_generation();
|
||||||
|
BLT_END_INTERVAL("Symbolic Regression", "Create Next Generation");
|
||||||
|
BLT_TRACE("Move to next generation");
|
||||||
|
BLT_START_INTERVAL("Symbolic Regress", "Move Next Generation");
|
||||||
|
program.next_generation();
|
||||||
|
BLT_END_INTERVAL("Symbolic Regress", "Move Next Generation");
|
||||||
|
BLT_TRACE("Evaluate Fitness");
|
||||||
|
BLT_START_INTERVAL("Symbolic Regress", "Evaluate Fitness");
|
||||||
|
program.evaluate_fitness();
|
||||||
|
BLT_END_INTERVAL("Symbolic Regress", "Evaluate Fitness");
|
||||||
|
BLT_START_INTERVAL("Symbolic Regress", "Fitness Print");
|
||||||
|
const auto& stats = program.get_population_stats();
|
||||||
|
BLT_TRACE("Avg Fit: {}, Best Fit: {}, Worst Fit: {}, Overall Fit: {}",
|
||||||
|
stats.average_fitness.load(std::memory_order_relaxed), stats.best_fitness.load(std::memory_order_relaxed),
|
||||||
|
stats.worst_fitness.load(std::memory_order_relaxed), stats.overall_fitness.load(std::memory_order_relaxed));
|
||||||
|
BLT_END_INTERVAL("Symbolic Regress", "Fitness Print");
|
||||||
|
#ifdef BLT_TRACK_ALLOCATIONS
|
||||||
|
crossover_calls.stop_measurement(cross);
|
||||||
|
mutation_calls.stop_measurement(mut);
|
||||||
|
reproduction_calls.stop_measurement(repo);
|
||||||
|
const auto total = (cross.get_call_difference() * 2) + mut.get_call_difference() + repo.get_call_difference();
|
||||||
|
BLT_TRACE("Calls Crossover: %ld, Mutation %ld, Reproduction %ld; %ld", cross.get_call_difference(), mut.get_call_difference(), repo.get_call_difference(), total);
|
||||||
|
BLT_TRACE("Value Crossover: %ld, Mutation %ld, Reproduction %ld; %ld", cross.get_value_difference(), mut.get_value_difference(), repo.get_value_difference(), (cross.get_value_difference() * 2 + mut.get_value_difference() + repo.get_value_difference()) - total);
|
||||||
|
#endif
|
||||||
|
BLT_TRACE("----------------------------------------------");
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
BLT_END_INTERVAL("Symbolic Regression", "Total Generation Loop");
|
||||||
|
|
||||||
|
const auto best = program.get_best_individuals<1>();
|
||||||
|
|
||||||
|
if (best[0].get().fitness.adjusted_fitness > best_fitness)
|
||||||
|
{
|
||||||
|
best_fitness = best[0].get().fitness.adjusted_fitness;
|
||||||
|
best_config = config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_run()
|
||||||
|
{
|
||||||
|
std::stringstream results;
|
||||||
|
for (const auto crossover_chance : crossover_chances)
|
||||||
|
{
|
||||||
|
for (const auto mutation_chance : mutation_chances)
|
||||||
|
{
|
||||||
|
for (const auto reproduction_chance : reproduction_chances)
|
||||||
|
{
|
||||||
|
if (crossover_chance == 0 && mutation_chance == 0 && reproduction_chance == 0)
|
||||||
|
continue;
|
||||||
|
for (const auto elite_amount : elite_amounts)
|
||||||
|
{
|
||||||
|
for (const auto population_sizes : population_sizes)
|
||||||
|
{
|
||||||
|
blt::gp::prog_config_t config = blt::gp::prog_config_t()
|
||||||
|
.set_initial_min_tree_size(2)
|
||||||
|
.set_initial_max_tree_size(6)
|
||||||
|
.set_elite_count(elite_amount)
|
||||||
|
.set_crossover_chance(crossover_chance)
|
||||||
|
.set_mutation_chance(mutation_chance)
|
||||||
|
.set_reproduction_chance(reproduction_chance)
|
||||||
|
.set_max_generations(50)
|
||||||
|
.set_pop_size(population_sizes)
|
||||||
|
.set_thread_count(0);
|
||||||
|
|
||||||
|
BLT_INFO("Run: Crossover ({}) Mutation ({}) Reproduction ({}) Elite ({}) Population Size ({})", crossover_chance,
|
||||||
|
mutation_chance, reproduction_chance, elite_amount, population_sizes);
|
||||||
|
run(config);
|
||||||
|
|
||||||
|
results << "Run: Crossover (";
|
||||||
|
results << crossover_chance;
|
||||||
|
results << ") Mutation (";
|
||||||
|
results << mutation_chance;
|
||||||
|
results << ") Reproduction (";
|
||||||
|
results << reproduction_chance;
|
||||||
|
results << ") Elite (";
|
||||||
|
results << elite_amount;
|
||||||
|
results << ") Population Size (";
|
||||||
|
results << population_sizes;
|
||||||
|
results << ")" << std::endl;
|
||||||
|
BLT_WRITE_PROFILE(results, "Symbolic Regression");
|
||||||
|
results << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << results.str() << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Best Configuration is: " << std::endl;
|
||||||
|
std::cout << "\tCrossover: " << best_config.crossover_chance << std::endl;
|
||||||
|
std::cout << "\tMutation: " << best_config.mutation_chance << std::endl;
|
||||||
|
std::cout << "\tReproduction: " << best_config.reproduction_chance << std::endl;
|
||||||
|
std::cout << "\tElites: " << best_config.elites << std::endl;
|
||||||
|
std::cout << "\tPopulation Size: " << best_config.population_size << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename What, typename What2>
|
||||||
|
auto what(What addr, What2 addr2) -> decltype(addr + addr2)
|
||||||
|
{
|
||||||
|
return addr + addr2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class test
|
||||||
|
{
|
||||||
|
hello,
|
||||||
|
there
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void hello(blt::size_t)
|
||||||
|
{
|
||||||
|
BLT_TRACE("I did some parallel work!");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void there(blt::size_t)
|
||||||
|
{
|
||||||
|
BLT_TRACE("Wow there");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// blt::gp::thread_manager_t threads{
|
||||||
|
// std::thread::hardware_concurrency(), blt::gp::task_builder_t<test>::make_callable(
|
||||||
|
// blt::gp::task_t{test::hello, hello},
|
||||||
|
// blt::gp::task_t{test::there, there}
|
||||||
|
// )
|
||||||
|
// };
|
||||||
|
|
||||||
|
// threads.add_task(test::hello);
|
||||||
|
// threads.add_task(test::hello);
|
||||||
|
// threads.add_task(test::hello);
|
||||||
|
// threads.add_task(test::there);
|
||||||
|
|
||||||
|
// while (threads.has_tasks_left())
|
||||||
|
// threads.execute();
|
||||||
|
|
||||||
|
for (int i = 0; i < 1; i++)
|
||||||
|
do_run();
|
||||||
|
BLT_PRINT_PROFILE("Symbolic Regress");
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1,41 +0,0 @@
|
||||||
Performance counter stats for './cmake-build-release/blt-symbolic-regression-example' (30 runs):
|
|
||||||
|
|
||||||
35,671,860,546 branches ( +- 5.05% ) (20.11%)
|
|
||||||
130,603,525 branch-misses # 0.37% of all branches ( +- 4.61% ) (20.67%)
|
|
||||||
43,684,408 cache-misses # 9.61% of all cache refs ( +- 3.08% ) (20.97%)
|
|
||||||
454,604,804 cache-references ( +- 4.53% ) (21.30%)
|
|
||||||
72,861,649,501 cycles ( +- 5.33% ) (22.00%)
|
|
||||||
170,811,735,018 instructions # 2.34 insn per cycle ( +- 5.59% ) (22.84%)
|
|
||||||
0 alignment-faults
|
|
||||||
33,002 cgroup-switches ( +- 1.71% )
|
|
||||||
293,932 faults ( +- 4.09% )
|
|
||||||
1,130,322,318 ns duration_time ( +- 3.73% )
|
|
||||||
16,750,942,537 ns user_time ( +- 1.71% )
|
|
||||||
1,165,192,903 ns system_time ( +- 0.87% )
|
|
||||||
57,551,179,178 L1-dcache-loads ( +- 5.63% ) (22.36%)
|
|
||||||
214,283,064 L1-dcache-load-misses # 0.37% of all L1-dcache accesses ( +- 5.58% ) (22.13%)
|
|
||||||
75,685,527 L1-dcache-prefetches ( +- 7.55% ) (22.07%)
|
|
||||||
1,115,360,458 L1-icache-loads ( +- 3.91% ) (21.67%)
|
|
||||||
2,868,754 L1-icache-load-misses # 0.26% of all L1-icache accesses ( +- 3.34% ) (21.34%)
|
|
||||||
65,107,178 dTLB-loads ( +- 8.94% ) (21.00%)
|
|
||||||
4,971,480 dTLB-load-misses # 7.64% of all dTLB cache accesses ( +- 3.70% ) (20.90%)
|
|
||||||
452,351 iTLB-loads ( +- 4.80% ) (20.62%)
|
|
||||||
1,600,933 iTLB-load-misses # 353.91% of all iTLB cache accesses ( +- 3.68% ) (20.62%)
|
|
||||||
332,075,460 l2_request_g1.all_no_prefetch ( +- 4.59% ) (20.73%)
|
|
||||||
293,932 page-faults ( +- 4.09% )
|
|
||||||
293,928 page-faults:u ( +- 4.09% )
|
|
||||||
3 page-faults:k ( +- 4.92% )
|
|
||||||
58,806,652,381 L1-dcache-loads ( +- 5.44% ) (20.61%)
|
|
||||||
216,591,223 L1-dcache-load-misses # 0.38% of all L1-dcache accesses ( +- 5.39% ) (21.02%)
|
|
||||||
<not supported> LLC-loads
|
|
||||||
<not supported> LLC-load-misses
|
|
||||||
1,059,748,012 L1-icache-loads ( +- 4.29% ) (21.55%)
|
|
||||||
2,615,017 L1-icache-load-misses # 0.23% of all L1-icache accesses ( +- 3.34% ) (21.85%)
|
|
||||||
65,917,126 dTLB-loads ( +- 8.89% ) (21.78%)
|
|
||||||
4,717,351 dTLB-load-misses # 7.25% of all dTLB cache accesses ( +- 3.52% ) (22.05%)
|
|
||||||
459,796 iTLB-loads ( +- 5.92% ) (21.77%)
|
|
||||||
1,512,986 iTLB-load-misses # 334.47% of all iTLB cache accesses ( +- 3.64% ) (21.26%)
|
|
||||||
74,656,433 L1-dcache-prefetches ( +- 7.94% ) (20.50%)
|
|
||||||
<not supported> L1-dcache-prefetch-misses
|
|
||||||
|
|
||||||
1.1303 +- 0.0422 seconds time elapsed ( +- 3.73% )
|
|
Loading…
Reference in New Issue