diff --git a/.idea/editor.xml b/.idea/editor.xml
index b0d69ef..04cdbc9 100644
--- a/.idea/editor.xml
+++ b/.idea/editor.xml
@@ -240,244 +240,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a8d5f4a..8dec521 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,7 +27,7 @@ macro(compile_options target_name)
sanitizers(${target_name})
endmacro()
-project(blt-gp VERSION 0.4.6)
+project(blt-gp VERSION 0.4.9)
include(CTest)
@@ -121,5 +121,6 @@ if (${BUILD_GP_TESTS})
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)
endif ()
\ No newline at end of file
diff --git a/examples/symbolic_regression.h b/examples/symbolic_regression.h
index 3c206b1..d89efbc 100644
--- a/examples/symbolic_regression.h
+++ b/examples/symbolic_regression.h
@@ -40,6 +40,7 @@ namespace blt::gp::example
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(fitness_case));
if (diff < value_cutoff)
{
diff --git a/lib/blt b/lib/blt
index 9a05c86..729a16a 160000
--- a/lib/blt
+++ b/lib/blt
@@ -1 +1 @@
-Subproject commit 9a05c86b02c9c45c2b384c531007416148ec4b56
+Subproject commit 729a16ab574e31bf1b44446a777e4ee834518c6e
diff --git a/src/transformers.cpp b/src/transformers.cpp
index 511ad74..b7bf704 100644
--- a/src/transformers.cpp
+++ b/src/transformers.cpp
@@ -144,15 +144,34 @@ namespace blt::gp
size_t mutation_t::mutate_point(gp_program& program, tree_t& c, const tree_t::subtree_point_t node) const
{
auto& new_tree = tree_t::get_thread_local(program);
+#if BLT_DEBUG_LEVEL >= 2
+ auto previous_size = new_tree.size();
+ auto previous_bytes = new_tree.total_value_bytes();
+#endif
config.generator.get().generate(new_tree, {program, node.type, config.replacement_min_depth, config.replacement_max_depth});
+#if BLT_DEBUG_LEVEL >= 2
+ const auto old_op = c.get_operator(node.pos);
+ if (!new_tree.check(detail::debug::context_ptr))
+ {
+ BLT_ERROR("Mutate point new tree check failed!");
+ BLT_ERROR("Old Op: {} got replaced with New Op: {}", program.get_name(old_op.id()).value_or("Unknown"),
+ program.get_name(new_tree.get_operator(0).id()).value_or("Unknown"));
+ BLT_ERROR("Tree started with size: {} and bytes: {}", previous_size, previous_bytes);
+ throw std::runtime_error("Mutate Point tree check failed");
+ }
+#endif
+
c.replace_subtree(node, new_tree);
// this will check to make sure that the tree is in a correct and executable state. it requires that the evaluation is context free!
#if BLT_DEBUG_LEVEL >= 2
+ const auto new_op = c.get_operator(node.pos);
if (!c.check(detail::debug::context_ptr))
{
print_mutate_stats();
+ BLT_ERROR("Old Op: {} got replaced with New Op: {}", program.get_name(old_op.id()).value_or("Unknown"),
+ program.get_name(new_op.id()).value_or("Unknown"));
throw std::runtime_error("Mutate Point tree check failed");
}
#endif
diff --git a/src/tree.cpp b/src/tree.cpp
index e866e82..7a45418 100644
--- a/src/tree.cpp
+++ b/src/tree.cpp
@@ -501,6 +501,7 @@ namespace blt::gp
tree_t& tree_t::get_thread_local(gp_program& program)
{
thread_local tree_t tree{program};
+ tree.clear(program);
return tree;
}
@@ -545,7 +546,7 @@ namespace blt::gp
if (bytes_expected != bytes_size)
{
BLT_ERROR("Stack state: {}", values.size());
- BLT_ERROR("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(bytes_expected) - static_cast(bytes_size));
BLT_ERROR("Amount of bytes in stack doesn't match the number of bytes expected for the operations");
return false;
@@ -588,18 +589,18 @@ namespace blt::gp
if (v1 != v2)
{
const auto vd = std::abs(v1 - v2);
- BLT_ERROR("found %ld bytes expected %ld bytes, total difference: %ld", v1, v2, vd);
- BLT_ERROR("Total Produced %ld || Total Consumed %ld || Total Difference %ld", total_produced, total_consumed,
+ 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(total_produced) - static_cast(total_consumed)));
return false;
}
}
catch (std::exception& e)
{
- BLT_ERROR("Exception occurred \"%s\"", e.what());
- BLT_ERROR("Total Produced %ld || Total Consumed %ld || Total Difference %ld", total_produced, total_consumed,
+ BLT_ERROR("Exception occurred \"{}\"", e.what());
+ BLT_ERROR("Total Produced {} || Total Consumed {} || Total Difference {}", total_produced, total_consumed,
std::abs(static_cast(total_produced) - static_cast(total_consumed)));
- BLT_ERROR("We failed at index %lu", index);
+ BLT_ERROR("We failed at index {}", index);
return false;
}
return true;
diff --git a/tests/2_type_drop_test.cpp b/tests/2_type_drop_test.cpp
new file mode 100644
index 0000000..69b0bf9
--- /dev/null
+++ b/tests/2_type_drop_test.cpp
@@ -0,0 +1,194 @@
+/*
+ *
+ * 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 .
+ */
+#include "../examples/symbolic_regression.h"
+#include
+#include
+
+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(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(fitness.hits) == regression.get_training_cases().size();
+}
+
+int main()
+{
+ operator_builder 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().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());
+}
diff --git a/tests/symbolic_regression_test.cpp b/tests/symbolic_regression_test.cpp
index 74219da..d7239ef 100644
--- a/tests/symbolic_regression_test.cpp
+++ b/tests/symbolic_regression_test.cpp
@@ -19,6 +19,7 @@
#include
#include
+#include
#include
static const auto SEED_FUNC = [] { return std::random_device()(); };
@@ -56,7 +57,7 @@ void run(const blt::gp::prog_config_t& config)
auto mut = mutation_calls.start_measurement();
auto repo = reproduction_calls.start_measurement();
#endif
- BLT_TRACE("------------{Begin Generation %ld}------------", program.get_current_generation());
+ 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();
@@ -71,7 +72,7 @@ void run(const blt::gp::prog_config_t& config)
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: %lf, Best Fit: %lf, Worst Fit: %lf, Overall Fit: %lf",
+ 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");
@@ -123,17 +124,8 @@ void do_run()
.set_pop_size(population_sizes)
.set_thread_count(0);
- BLT_INFO_STREAM << "Run: Crossover (";
- BLT_INFO_STREAM << crossover_chance;
- BLT_INFO_STREAM << ") Mutation (";
- BLT_INFO_STREAM << mutation_chance;
- BLT_INFO_STREAM << ") Reproduction (";
- BLT_INFO_STREAM << reproduction_chance;
- BLT_INFO_STREAM << ") Elite (";
- BLT_INFO_STREAM << elite_amount;
- BLT_INFO_STREAM << ") Population Size (";
- BLT_INFO_STREAM << population_sizes;
- BLT_INFO_STREAM << ")" << "\n";
+ BLT_INFO("Run: Crossover ({}) Mutation ({}) Reproduction ({}) Elite ({}) Population Size ({})", crossover_chance,
+ mutation_chance, reproduction_chance, elite_amount, population_sizes);
run(config);
results << "Run: Crossover (";
@@ -190,10 +182,10 @@ inline void there(blt::size_t)
int main()
{
// blt::gp::thread_manager_t threads{
- // std::thread::hardware_concurrency(), blt::gp::task_builder_t::make_callable(
- // blt::gp::task_t{test::hello, hello},
- // blt::gp::task_t{test::there, there}
- // )
+ // std::thread::hardware_concurrency(), blt::gp::task_builder_t::make_callable(
+ // blt::gp::task_t{test::hello, hello},
+ // blt::gp::task_t{test::there, there}
+ // )
// };
// threads.add_task(test::hello);
@@ -202,7 +194,7 @@ int main()
// threads.add_task(test::there);
// while (threads.has_tasks_left())
- // threads.execute();
+ // threads.execute();
for (int i = 0; i < 1; i++)
do_run();