diff --git a/.idea/workspace (conflicted copy 2024-11-14 160344).xml b/.idea/workspace (conflicted copy 2024-11-14 160344).xml
deleted file mode 100644
index fa556d1..0000000
--- a/.idea/workspace (conflicted copy 2024-11-14 160344).xml
+++ /dev/null
@@ -1,229 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- "useNewFormat": true
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- "associatedIndex": 7
-}
-
-
-
-
-
- {
- "keyToString": {
- "CMake Application.COSC-4P80-Assignment-3.executor": "Run",
- "NIXITCH_NIXPKGS_CONFIG": "/etc/nix/nixpkgs-config.nix",
- "NIXITCH_NIX_CONF_DIR": "",
- "NIXITCH_NIX_OTHER_STORES": "",
- "NIXITCH_NIX_PATH": "/home/brett/.nix-defexpr/channels:nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels",
- "NIXITCH_NIX_PROFILES": "/run/current-system/sw /nix/var/nix/profiles/default /etc/profiles/per-user/brett /home/brett/.local/state/nix/profile /nix/profile /home/brett/.nix-profile",
- "NIXITCH_NIX_REMOTE": "",
- "NIXITCH_NIX_USER_PROFILE_DIR": "/nix/var/nix/profiles/per-user/brett",
- "RunOnceActivity.RadMigrateCodeStyle": "true",
- "RunOnceActivity.ShowReadmeOnStart": "true",
- "RunOnceActivity.cidr.known.project.marker": "true",
- "RunOnceActivity.readMode.enableVisualFormatting": "true",
- "SHARE_PROJECT_CONFIGURATION_FILES": "true",
- "cf.advertisement.text.has.clang-format": "true",
- "cf.first.check.clang-format": "false",
- "cidr.known.project.marker": "true",
- "git-widget-placeholder": "main",
- "last_opened_file_path": "/home/brett/Documents/Brock/CS 4P80/COSC-4P80-Assignment-3",
- "node.js.detected.package.eslint": "true",
- "node.js.detected.package.tslint": "true",
- "node.js.selected.package.eslint": "(autodetect)",
- "node.js.selected.package.tslint": "(autodetect)",
- "nodejs_package_manager_path": "npm",
- "run.code.analysis.last.selected.profile": "pProject Default",
- "settings.editor.selected.configurable": "preferences.sourceCode.C/C++",
- "structure.view.defaults.are.configured": "true",
- "vue.rearranger.settings.migration": "true"
- }
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 1730483030448
-
-
- 1730483030448
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0349dd3..79e0868 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.15)
+project(COSC-4P80-Assignment-3 VERSION 0.0.16)
include(FetchContent)
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
diff --git a/include/assign3/array.h b/include/assign3/array.h
index d220caa..b1e1479 100644
--- a/include/assign3/array.h
+++ b/include/assign3/array.h
@@ -21,6 +21,7 @@
#include
#include
+#include
namespace assign3
{
@@ -36,7 +37,7 @@ namespace assign3
map.emplace_back(dimensions, (j % 2 == 0 ? static_cast(i) : static_cast(i) + 0.5f), j);
}
- [[nodiscard]] std::pair from_index(blt::size_t index) const
+ [[nodiscard]] blt::vec2ul from_index(blt::size_t index) const
{
return {index % width, index / width};
}
diff --git a/include/assign3/fwdecl.h b/include/assign3/fwdecl.h
index 74eb791..f389c7d 100644
--- a/include/assign3/fwdecl.h
+++ b/include/assign3/fwdecl.h
@@ -20,6 +20,8 @@
#define COSC_4P80_ASSIGNMENT_3_FWDECL_H
#include
+#include
+#include
namespace assign3
{
@@ -38,6 +40,17 @@ namespace assign3
TOROIDAL = RENDER_3D,
CYLINDER = RENDER_3D
};
+
+ enum class debug_type
+ {
+ DATA_POINT,
+ DISTANCE
+ };
+
+ inline std::array debug_names {
+ "Distance to Datapoint",
+ "Distance to Neighbours"
+ };
}
#endif //COSC_4P80_ASSIGNMENT_3_FWDECL_H
diff --git a/include/assign3/manager.h b/include/assign3/manager.h
index 86e03fb..7e7937c 100644
--- a/include/assign3/manager.h
+++ b/include/assign3/manager.h
@@ -30,16 +30,19 @@ namespace assign3
{
class renderer_t;
- struct render_info_t
+ struct neuron_render_info_t
{
blt::vec2 base_pos;
blt::vec2 neuron_padding;
float neuron_scale = 0;
- static render_info_t fill_screen(renderer_t& renderer, float neuron_scale = 35, float x_padding = 250, float y_padding = 50, float w_padding = 50, float h_padding = 50);
+ static neuron_render_info_t fill_screen(renderer_t& renderer, float neuron_scale = 35, float x_padding = 250, float y_padding = 50,
+ float w_padding = 50, float h_padding = 50);
BLT_LVALUE_SETTER(blt::vec2, base_pos);
+
BLT_LVALUE_SETTER(blt::vec2, neuron_padding);
+
BLT_PRVALUE_SETTER(float, neuron_scale);
};
@@ -52,11 +55,24 @@ namespace assign3
void update();
};
+ struct render_data_t
+ {
+ blt::size_t index;
+ const neuron_t& neuron;
+ blt::vec2 neuron_scaled;
+ blt::vec2 neuron_offset;
+ blt::vec2 neuron_padded;
+
+ render_data_t(size_t index, const neuron_t& neuron, const blt::vec2& neuronScaled, const blt::vec2& neuronOffset,
+ const blt::vec2& neuronPadded):
+ index(index), neuron(neuron), neuron_scaled(neuronScaled), neuron_offset(neuronOffset), neuron_padded(neuronPadded)
+ {}
+ };
class renderer_t
{
friend motor_data_t;
- friend render_info_t;
+ friend neuron_render_info_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}
@@ -66,7 +82,11 @@ namespace assign3
void cleanup();
- void draw_som(const std::function& color_func, bool debug);
+ std::vector get_neuron_activations(const data_file_t& file);
+
+ void draw_som(neuron_render_info_t info, const std::function& color_func);
+
+ void draw_debug(const data_file_t& file);
void render();
@@ -95,6 +115,10 @@ namespace assign3
Scalar initial_learn_rate = 0.1;
int currently_selected_network = 0;
+ bool debug_mode = false;
+ bool running = false;
+ int debug_state = 0;
+ int selected_data_point = 0;
};
}
diff --git a/lib/blt-with-graphics b/lib/blt-with-graphics
index 0a0b87f..3610837 160000
--- a/lib/blt-with-graphics
+++ b/lib/blt-with-graphics
@@ -1 +1 @@
-Subproject commit 0a0b87fc5566211cb7375d9c1b361ba3e91a3545
+Subproject commit 361083780325a2a73c9467320dcd5c7ced51e754
diff --git a/src/main.cpp b/src/main.cpp
index 5380d94..318e244 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -3,6 +3,7 @@
#include
#include "blt/gfx/renderer/resource_manager.h"
#include "blt/gfx/renderer/camera.h"
+#include "implot.h"
#include
#include
@@ -22,6 +23,8 @@ void init(const blt::gfx::window_data&)
global_matrices.create_internals();
resources.load_resources();
renderer.create();
+
+ ImPlot::CreateContext();
}
void update(const blt::gfx::window_data& window_data)
@@ -41,6 +44,7 @@ void destroy(const blt::gfx::window_data&)
global_matrices.cleanup();
resources.cleanup();
renderer.cleanup();
+ ImPlot::DestroyContext();
blt::gfx::cleanup();
BLT_INFO("Goodbye World!");
}
diff --git a/src/manager.cpp b/src/manager.cpp
index 8c21253..12d327e 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -17,21 +17,35 @@
*/
#include
#include
+#include
#include
#include
+static void HelpMarker(const std::string& desc)
+{
+ ImGui::TextDisabled("(?)");
+ if (ImGui::BeginItemTooltip())
+ {
+ ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
+ ImGui::TextUnformatted(desc.c_str());
+ ImGui::PopTextWrapPos();
+ ImGui::EndTooltip();
+ }
+}
+
const char* get_selection_string(void* user_data, int selection)
{
- return (*reinterpret_cast*>(user_data))[selection].c_str();
+ return (reinterpret_cast(user_data))[selection].c_str();
}
namespace assign3
{
- render_info_t render_info_t::fill_screen(renderer_t& renderer, float neuron_scale, float x_padding, float y_padding, float w_padding,
- float h_padding)
+ neuron_render_info_t neuron_render_info_t::fill_screen(renderer_t& renderer, float neuron_scale, float x_padding, float y_padding,
+ float w_padding,
+ float h_padding)
{
- render_info_t info;
+ neuron_render_info_t info;
info.set_neuron_scale(neuron_scale);
info.set_base_pos({x_padding, y_padding});
@@ -44,7 +58,9 @@ namespace assign3
float remain_width = screen_width - neuron_width;
float remain_height = screen_height - neuron_height;
- info.set_neuron_padding({remain_width / static_cast(renderer.som_width), remain_height / static_cast(renderer.som_height)});
+ float remain = std::min(remain_width, remain_height);
+
+ info.set_neuron_padding({remain / static_cast(renderer.som_width), remain / static_cast(renderer.som_height)});
return info;
}
@@ -57,9 +73,11 @@ namespace assign3
void renderer_t::create()
{
- fr2d.create(250, 2048);
+ fr2d.create_default(250, 2048);
br2d.create();
+ topology_function = std::make_unique();
+
generate_network(currently_selected_network);
update_graphics();
}
@@ -70,150 +88,90 @@ namespace assign3
br2d.cleanup();
}
- void renderer_t::draw_som(const std::function& color_func, bool debug)
+ void renderer_t::draw_som(neuron_render_info_t info, const std::function& color_func)
{
-
+ for (const auto& [i, neuron] : blt::enumerate(som->get_array().get_map()))
+ {
+ blt::vec2 neuron_pos = {neuron.get_x(), neuron.get_y()};
+ auto neuron_scaled = neuron_pos * info.neuron_scale;
+ auto neuron_offset = neuron_scaled + info.base_pos;
+ auto neuron_padded = neuron_offset + neuron_pos * info.neuron_padding;
+
+ auto color = color_func({i, neuron, neuron_scaled, neuron_offset, neuron_padded});
+ br2d.drawPointInternal(color, blt::gfx::point2d_t{neuron_padded, info.neuron_scale});
+ }
}
void renderer_t::render()
{
using namespace blt::gfx;
+ ImGui::ShowDemoWindow();
+ ImPlot::ShowDemoWindow();
+
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"))
+ ImGui::SetNextItemOpen(true, ImGuiCond_Appearing);
+ if (ImGui::CollapsingHeader("SOM Control"))
{
- 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())
+ ImGui::Text("Network Select");
+ if (ImGui::ListBox("##Network Select", ¤tly_selected_network, get_selection_string, motor_data.map_files_names.data(),
+ 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());
+ ImGui::Checkbox("Run to completion", &running);
+ ImGui::Text("Epoch %ld / %ld", som->get_current_epoch(), som->get_max_epochs());
+ }
+ if (ImGui::CollapsingHeader("Debug"))
+ {
+ ImGui::Checkbox("Debug Visuals", &debug_mode);
+ if (debug_mode)
+ {
+ ImGui::ListBox("##DebugStateSelect", &debug_state, get_selection_string, debug_names.data(), debug_names.size());
+ switch (static_cast(debug_state))
+ {
+ case debug_type::DATA_POINT:
+ {
+ auto current_data_file = motor_data.files[currently_selected_network].normalize();
+ std::vector names;
+ for (const auto& [i, v] : blt::enumerate(current_data_file.data_points))
+ names.push_back("#" + std::to_string(i) + " (" + (v.is_bad ? "Bad)" : "Good)"));
+ ImGui::Text("Select Data Point");
+ ImGui::ListBox("##SelectDataPoint", &selected_data_point, get_selection_string, names.data(),
+ static_cast(names.size()));
+ }
+ break;
+ case debug_type::DISTANCE:
+
+ break;
+ }
+ }
}
- 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());
+ if (running)
+ {
+ if (som->get_current_epoch() < som->get_max_epochs())
+ som->train_epoch(initial_learn_rate, topology_function.get());
+ }
auto current_data_file = motor_data.files[currently_selected_network].normalize();
- for (auto& v : current_data_file.data_points)
+
+ if (!debug_mode)
{
- const auto nearest = som->get_closest_neuron(v.bins);
- activations[nearest] += v.is_bad ? -1 : 1;
- }
+ auto closest_type = get_neuron_activations(current_data_file);
+ draw_som(neuron_render_info_t{}.set_base_pos({370, 145}).set_neuron_scale(120).set_neuron_padding({5, 5}),
+ [&closest_type](render_data_t context) {
+ auto type = closest_type[context.index];
+ return type >= 0 ? blt::make_color(0, type, 0) : blt::make_color(-type, 0, 0);
+ });
+ } else
+ draw_debug(current_data_file);
- 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);
+ br2d.render(0, 0);
fr2d.render();
}
@@ -231,9 +189,66 @@ namespace assign3
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;
+ draw_width = (max_x - min_x);
+ draw_height = (max_y - min_y);
+ }
+
+ std::vector renderer_t::get_neuron_activations(const data_file_t& file)
+ {
+ static std::vector closest_type;
+ closest_type.clear();
+ closest_type.resize(som->get_array().get_map().size());
- topology_function = std::make_unique();
+ 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 : file.data_points)
+ {
+ auto dist = v.dist(data.bins);
+ auto ds = topology_function->call(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;
+ }
+
+ return closest_type;
+ }
+
+ void renderer_t::draw_debug(const data_file_t& file)
+ {
+ switch (static_cast(debug_state))
+ {
+ case debug_type::DATA_POINT:
+ {
+ const auto& data_point = file.data_points[selected_data_point];
+ auto closest_type = get_neuron_activations(file);
+ draw_som(neuron_render_info_t{}.set_base_pos({370, 145}).set_neuron_scale(120).set_neuron_padding({50, 50}),
+ [this, &closest_type](render_data_t context) {
+ auto& text = fr2d.render_text(std::to_string(closest_type[context.index]), 13);
+ auto text_width = text.getAssociatedText().getTextWidth();
+ auto text_height = text.getAssociatedText().getTextHeight();
+ text.setPosition(context.neuron_padded - blt::vec2{text_width / 2.0f, text_height / 2.0f}).setZIndex(1);
+
+ auto type = closest_type[context.index];
+ return type >= 0 ? blt::make_color(0, type, 0) : blt::make_color(-type, 0, 0);
+ });
+ }
+ break;
+ case debug_type::DISTANCE:
+ {
+
+ }
+ break;
+ }
}
}
\ No newline at end of file