diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..f63addd --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/editor.xml b/.idea/editor.xml new file mode 100644 index 0000000..fde0314 --- /dev/null +++ b/.idea/editor.xml @@ -0,0 +1,580 @@ + + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 64f799a..0349dd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(COSC-4P80-Assignment-3 VERSION 0.0.14) +project(COSC-4P80-Assignment-3 VERSION 0.0.15) include(FetchContent) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) diff --git a/include/assign3/manager.h b/include/assign3/manager.h new file mode 100644 index 0000000..062369b --- /dev/null +++ b/include/assign3/manager.h @@ -0,0 +1,93 @@ +#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 . + */ + +#ifndef COSC_4P80_ASSIGNMENT_3_UI_H +#define COSC_4P80_ASSIGNMENT_3_UI_H + +#include "blt/gfx/renderer/batch_2d_renderer.h" +#include "blt/gfx/renderer/font_renderer.h" +#include +#include +#include + +namespace assign3 +{ + + struct render_info_t + { + blt::vec2 base_pos; + blt::vec2 neuron_padding; + float neuron_scale = 0; + }; + + class motor_data_t + { + public: + std::vector files; + std::vector map_files_names; + + void update(); + }; + + + class renderer_t + { + friend motor_data_t; + public: + explicit renderer_t(motor_data_t& data, blt::gfx::resource_manager& resources, blt::gfx::matrix_state_manager& state): + motor_data(data), br2d{resources, state} + {} + + void create(); + + void cleanup(); + + void draw_som(const std::function& color_func, bool debug); + + void render(); + + void update_graphics(); + + void generate_network(int selection) + { + som = std::make_unique(motor_data.files[selection].normalize(), som_width, som_height, max_epochs); + } + + private: + motor_data_t& motor_data; + std::unique_ptr som; + std::unique_ptr topology_function; + + blt::gfx::font_renderer_t fr2d{}; + blt::gfx::batch_renderer_2d br2d; + + float draw_width = 0; + float draw_height = 0; + float neuron_scale = 35; + + blt::size_t som_width = 5; + blt::size_t som_height = 5; + blt::size_t max_epochs = 100; + Scalar initial_learn_rate = 0.1; + + int currently_selected_network = 0; + }; + +} + +#endif //COSC_4P80_ASSIGNMENT_3_UI_H diff --git a/include/assign3/ui.h b/include/assign3/ui.h deleted file mode 100644 index 163929e..0000000 --- a/include/assign3/ui.h +++ /dev/null @@ -1,26 +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 . - */ - -#ifndef COSC_4P80_ASSIGNMENT_3_UI_H -#define COSC_4P80_ASSIGNMENT_3_UI_H - -namespace assign3 { - -} - -#endif //COSC_4P80_ASSIGNMENT_3_UI_H diff --git a/lib/blt-with-graphics b/lib/blt-with-graphics index f75cbbb..0a0b87f 160000 --- a/lib/blt-with-graphics +++ b/lib/blt-with-graphics @@ -1 +1 @@ -Subproject commit f75cbbb10c3c5c482182def61365e6b7fe6da3af +Subproject commit 0a0b87fc5566211cb7375d9c1b361ba3e91a3545 diff --git a/src/main.cpp b/src/main.cpp index ddea90a..8a55f33 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,67 +3,17 @@ #include #include #include "blt/gfx/renderer/resource_manager.h" -#include "blt/gfx/renderer/batch_2d_renderer.h" -#include "blt/gfx/renderer/font_renderer.h" #include "blt/gfx/renderer/camera.h" -#include "blt/std/random.h" #include -#include -#include +#include using namespace assign3; -std::vector files; -std::unique_ptr som; - blt::gfx::matrix_state_manager global_matrices; blt::gfx::resource_manager resources; -blt::gfx::batch_renderer_2d renderer_2d(resources, global_matrices); blt::gfx::first_person_camera_2d camera; -blt::gfx::font_renderer_t font_renderer; - -blt::size_t som_width = 5; -blt::size_t som_height = 5; -blt::size_t max_epochs = 100; -Scalar initial_learn_rate = 0.1; - -int currently_selected_network = 0; -std::vector map_files_names; -std::unique_ptr topology_function; - -float neuron_scale = 35; -float draw_width; -float draw_height; - -void update_graphics() -{ - // find the min x / y for the currently drawn som as positions may depend on type. - const auto x_comparator = [](const auto& a, const auto& b) { - return a.get_x() < b.get_x(); - }; - const auto y_comparator = [](const auto& a, const auto& b) { - return a.get_y() < b.get_y(); - }; - const auto& som_neurons = som->get_array().get_map(); - auto min_x = std::min_element(som_neurons.begin(), som_neurons.end(), x_comparator)->get_x(); - auto max_x = std::max_element(som_neurons.begin(), som_neurons.end(), x_comparator)->get_x(); - auto min_y = std::min_element(som_neurons.begin(), som_neurons.end(), y_comparator)->get_y(); - auto max_y = std::max_element(som_neurons.begin(), som_neurons.end(), y_comparator)->get_y(); - draw_width = (max_x - min_x) * neuron_scale; - draw_height = (max_y - min_y) * neuron_scale; - - topology_function = std::make_unique(); -} - -void generate_network(int selection) -{ - som = std::make_unique(files[selection].normalize(), som_width, som_height, max_epochs); -} - -const char* get_selection_string(void*, int selection) -{ - return map_files_names[selection].c_str(); -} +assign3::motor_data_t data{}; +assign3::renderer_t renderer{data, resources, global_matrices}; void init(const blt::gfx::window_data&) { @@ -72,15 +22,7 @@ void init(const blt::gfx::window_data&) global_matrices.create_internals(); resources.load_resources(); - renderer_2d.create(); - font_renderer.create_default(250, 2048); - - font_renderer.create_text("Hello There \"I'm a boy", 13)->setPosition(50, 500); - - for (const auto& data : files) - map_files_names.emplace_back(std::to_string(data.data_points.begin()->bins.size())); - - generate_network(currently_selected_network); + renderer.create(); } void update(const blt::gfx::window_data& window_data) @@ -92,150 +34,14 @@ void update(const blt::gfx::window_data& window_data) camera.update_view(global_matrices); global_matrices.update(); - update_graphics(); - - if (ImGui::Begin("Controls")) - { - ImGui::Text("Network Select"); - if (ImGui::ListBox("##Network Select", ¤tly_selected_network, get_selection_string, nullptr, static_cast(map_files_names.size()))) - generate_network(currently_selected_network); - - if (ImGui::Button("Run Epoch")) - { - som->train_epoch(initial_learn_rate, topology_function.get()); - } - static bool run; - ImGui::Checkbox("Run to completion", &run); - if (run) - { - if (som->get_current_epoch() < som->get_max_epochs()) - som->train_epoch(initial_learn_rate, topology_function.get()); - } - ImGui::Text("Epoch %ld / %ld", som->get_current_epoch(), som->get_max_epochs()); - } - ImGui::End(); - - static std::vector activations; - - activations.clear(); - activations.resize(som->get_array().get_map().size()); - - auto current_data_file = files[currently_selected_network].normalize(); - for (auto& v : current_data_file.data_points) - { - auto nearest = som->get_closest_neuron(v.bins); - activations[nearest] += v.is_bad ? -1 : 1; - } - - blt::i64 max = *std::max_element(activations.begin(), activations.end()); - blt::i64 min = *std::min_element(activations.begin(), activations.end()); - - for (auto [i, v] : blt::enumerate(som->get_array().get_map())) - { - auto activation = activations[i]; - - blt::vec4 color = blt::make_color(1, 1, 1); - if (activation > 0) - color = blt::make_color(0, static_cast(activation) / static_cast(max), 0); - else if (activation < 0) - color = blt::make_color(std::abs(static_cast(activation) / static_cast(min)), 0, 0); - - renderer_2d.drawPointInternal(color, - point2d_t{v.get_x() * neuron_scale + neuron_scale, v.get_y() * neuron_scale + neuron_scale, neuron_scale}); - } - - static std::vector closest_type; - closest_type.clear(); - closest_type.resize(som->get_array().get_map().size()); - - for (auto [i, v] : blt::enumerate(som->get_array().get_map())) - { - Scalar lowest_distance = std::numeric_limits::max(); - bool is_bad = false; - for (const auto& data : current_data_file.data_points) - { - auto dist = v.dist(data.bins); - if (dist < lowest_distance) - { - lowest_distance = dist; - is_bad = data.is_bad; - } - } -// BLT_TRACE(is_bad ? -lowest_distance : lowest_distance); - closest_type[i] = is_bad ? -lowest_distance : lowest_distance; - } - - auto min_dist = *std::min_element(closest_type.begin(), closest_type.end()); - auto max_dist = *std::max_element(closest_type.begin(), closest_type.end()); - - for (auto [i, v] : blt::enumerate(som->get_array().get_map())) - { - auto type = closest_type[i]; - - blt::vec4 color = blt::make_color(1, 1, 1); - if (type >= 0) - color = blt::make_color(0, 1 - (type / max_dist) + 0.1f, 0); - else if (type < 0) - color = blt::make_color(1 - (type / min_dist) + 0.1f, 0, 0); - - renderer_2d.drawPointInternal(color, - point2d_t{draw_width + neuron_scale * 2 + v.get_x() * neuron_scale + neuron_scale, - v.get_y() * neuron_scale + neuron_scale, neuron_scale}); - } - - closest_type.clear(); - closest_type.resize(som->get_array().get_map().size()); - for (auto [i, v] : blt::enumerate(som->get_array().get_map())) - { - auto half = som->find_closest_neighbour_distance(i); - auto scale = topology_function->scale(half * 0.5f, 0.5); - for (const auto& data : current_data_file.data_points) - { - auto dist = v.dist(data.bins); - auto ds = topology_function->call(dist, scale); -// BLT_TRACE("%f, %f, %f", ds, dist, scale); - if (data.is_bad) - closest_type[i] -= ds; - else - closest_type[i] += ds; - } - } - auto min_act = *std::min_element(closest_type.begin(), closest_type.end()); - auto max_act = *std::max_element(closest_type.begin(), closest_type.end()); - for (auto& v : closest_type) - { - auto n = 2 * (v - min_act) / (max_act - min_act) - 1; - v = n; - } -// BLT_TRACE("Min %f Max %f", min_act, max_act); - - for (auto [i, v] : blt::enumerate(som->get_array().get_map())) - { - auto type = closest_type[i]; - - blt::vec4 color; - if (type >= 0) - color = blt::make_color(0, type, 0); - else - color = blt::make_color(-type, 0, 0); - - renderer_2d.drawPointInternal(color, - point2d_t{draw_width + neuron_scale * 2 + v.get_x() * neuron_scale + neuron_scale, - draw_height + neuron_scale * 2 + v.get_y() * neuron_scale + neuron_scale, neuron_scale}); - } - - font_renderer.render_text("Wow I hate you too", 32)->setPosition(100, 300); - - renderer_2d.render(window_data.width, window_data.height); - font_renderer.render(); + renderer.render(); } void destroy(const blt::gfx::window_data&) { global_matrices.cleanup(); resources.cleanup(); - renderer_2d.cleanup(); - font_renderer.cleanup(); + renderer.cleanup(); blt::gfx::cleanup(); BLT_INFO("Goodbye World!"); } @@ -248,7 +54,8 @@ int main(int argc, const char** argv) auto args = parser.parse_args(argc, argv); - files = assign3::data_file_t::load_data_files_from_path(args.get("file")); + data.files = assign3::data_file_t::load_data_files_from_path(args.get("file")); + data.update(); blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, destroy}.setSyncInterval(1).setMaximized(true)); } diff --git a/src/manager.cpp b/src/manager.cpp new file mode 100644 index 0000000..9ec1e61 --- /dev/null +++ b/src/manager.cpp @@ -0,0 +1,217 @@ +/* + * + * 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 . + */ +#include +#include +#include + +const char* get_selection_string(void* user_data, int selection) +{ + return (*reinterpret_cast*>(user_data))[selection].c_str(); +} + +namespace assign3 +{ + + void motor_data_t::update() + { + for (const auto& data : files) + map_files_names.emplace_back(std::to_string(data.data_points.begin()->bins.size())); + } + + void renderer_t::create() + { + fr2d.create(250, 2048); + br2d.create(); + + generate_network(currently_selected_network); + update_graphics(); + } + + void renderer_t::cleanup() + { + fr2d.cleanup(); + br2d.cleanup(); + } + + void renderer_t::draw_som(const std::function& color_func, bool debug) + { + + } + + void renderer_t::render() + { + using namespace blt::gfx; + + if (ImGui::Begin("Controls")) + { + ImGui::Text("Network Select"); + if (ImGui::ListBox("##Network Select", ¤tly_selected_network, get_selection_string, &motor_data.map_files_names, + static_cast(motor_data.map_files_names.size()))) + generate_network(currently_selected_network); + + if (ImGui::Button("Run Epoch")) + { + som->train_epoch(initial_learn_rate, topology_function.get()); + } + static bool run; + ImGui::Checkbox("Run to completion", &run); + if (run) + { + if (som->get_current_epoch() < som->get_max_epochs()) + som->train_epoch(initial_learn_rate, topology_function.get()); + } + ImGui::Text("Epoch %ld / %ld", som->get_current_epoch(), som->get_max_epochs()); + } + ImGui::End(); + + static std::vector activations; + + activations.clear(); + activations.resize(som->get_array().get_map().size()); + + auto current_data_file = motor_data.files[currently_selected_network].normalize(); + for (auto& v : current_data_file.data_points) + { + const auto nearest = som->get_closest_neuron(v.bins); + activations[nearest] += v.is_bad ? -1 : 1; + } + + const blt::i64 max = *std::max_element(activations.begin(), activations.end()); + const blt::i64 min = *std::min_element(activations.begin(), activations.end()); + + for (auto [i, v] : blt::enumerate(som->get_array().get_map())) + { + const auto activation = activations[i]; + + blt::vec4 color = blt::make_color(1, 1, 1); + if (activation > 0) + color = blt::make_color(0, static_cast(activation) / static_cast(max), 0); + else if (activation < 0) + color = blt::make_color(std::abs(static_cast(activation) / static_cast(min)), 0, 0); + + br2d.drawPointInternal(color, point2d_t{v.get_x() * neuron_scale + neuron_scale, v.get_y() * neuron_scale + neuron_scale, neuron_scale}); + } + + static std::vector closest_type; + closest_type.clear(); + closest_type.resize(som->get_array().get_map().size()); + + for (auto [i, v] : blt::enumerate(som->get_array().get_map())) + { + Scalar lowest_distance = std::numeric_limits::max(); + bool is_bad = false; + for (const auto& [is_bins_bad, bins] : current_data_file.data_points) + { + if (const auto dist = v.dist(bins); dist < lowest_distance) + { + lowest_distance = dist; + is_bad = is_bins_bad; + } + } + // BLT_TRACE(is_bad ? -lowest_distance : lowest_distance); + closest_type[i] = is_bad ? -lowest_distance : lowest_distance; + } + + auto min_dist = *std::min_element(closest_type.begin(), closest_type.end()); + auto max_dist = *std::max_element(closest_type.begin(), closest_type.end()); + + for (auto [i, v] : blt::enumerate(som->get_array().get_map())) + { + auto type = closest_type[i]; + + blt::vec4 color = blt::make_color(1, 1, 1); + if (type >= 0) + color = blt::make_color(0, 1 - (type / max_dist) + 0.1f, 0); + else if (type < 0) + color = blt::make_color(1 - (type / min_dist) + 0.1f, 0, 0); + + br2d.drawPointInternal(color, + point2d_t{ + draw_width + neuron_scale * 2 + v.get_x() * neuron_scale + neuron_scale, + v.get_y() * neuron_scale + neuron_scale, neuron_scale + }); + } + + closest_type.clear(); + closest_type.resize(som->get_array().get_map().size()); + for (auto [i, v] : blt::enumerate(som->get_array().get_map())) + { + auto half = som->find_closest_neighbour_distance(i); + auto scale = topology_function->scale(half * 0.5f, 0.5); + for (const auto& data : current_data_file.data_points) + { + auto dist = v.dist(data.bins); + auto ds = topology_function->call(dist, scale); + // BLT_TRACE("%f, %f, %f", ds, dist, scale); + if (data.is_bad) + closest_type[i] -= ds; + else + closest_type[i] += ds; + } + } + auto min_act = *std::min_element(closest_type.begin(), closest_type.end()); + auto max_act = *std::max_element(closest_type.begin(), closest_type.end()); + for (auto& v : closest_type) + { + auto n = 2 * (v - min_act) / (max_act - min_act) - 1; + v = n; + } + // BLT_TRACE("Min %f Max %f", min_act, max_act); + + for (auto [i, v] : blt::enumerate(som->get_array().get_map())) + { + auto type = closest_type[i]; + + blt::vec4 color; + if (type >= 0) + color = blt::make_color(0, type, 0); + else + color = blt::make_color(-type, 0, 0); + + br2d.drawPointInternal(color, + point2d_t{ + draw_width + neuron_scale * 2 + v.get_x() * neuron_scale + neuron_scale, + draw_height + neuron_scale * 2 + v.get_y() * neuron_scale + neuron_scale, neuron_scale + }); + } + + + br2d.render(0,0); + fr2d.render(); + } + + void renderer_t::update_graphics() + { + // find the min x / y for the currently drawn som as positions may depend on type. + const auto x_comparator = [](const auto& a, const auto& b) { + return a.get_x() < b.get_x(); + }; + const auto y_comparator = [](const auto& a, const auto& b) { + return a.get_y() < b.get_y(); + }; + const auto& som_neurons = som->get_array().get_map(); + auto min_x = std::min_element(som_neurons.begin(), som_neurons.end(), x_comparator)->get_x(); + auto max_x = std::max_element(som_neurons.begin(), som_neurons.end(), x_comparator)->get_x(); + auto min_y = std::min_element(som_neurons.begin(), som_neurons.end(), y_comparator)->get_y(); + auto max_y = std::max_element(som_neurons.begin(), som_neurons.end(), y_comparator)->get_y(); + draw_width = (max_x - min_x) * neuron_scale; + draw_height = (max_y - min_y) * neuron_scale; + + topology_function = std::make_unique(); + } +} \ No newline at end of file