diff --git a/.idea/workspace (conflicted copy 2024-11-18 173055).xml b/.idea/workspace (conflicted copy 2024-11-18 173055).xml
new file mode 100644
index 0000000..7688f0a
--- /dev/null
+++ b/.idea/workspace (conflicted copy 2024-11-18 173055).xml
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ "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 a7b9ac7..ba280ad 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.20)
+project(COSC-4P80-Assignment-3 VERSION 0.0.21)
include(FetchContent)
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
diff --git a/build_emscripten.sh b/build_emscripten.sh
old mode 100755
new mode 100644
diff --git a/include/assign3/array.h b/include/assign3/array.h
index 63cfaed..c2085fb 100644
--- a/include/assign3/array.h
+++ b/include/assign3/array.h
@@ -44,8 +44,8 @@ namespace assign3
case shape_t::GRID_OFFSET_WRAP:
for (blt::size_t j = 0; j < height; j++)
for (blt::size_t i = 0; i < width; i++)
- map.emplace_back(dimensions, (j % 2 == 0 ? static_cast(i) : static_cast(i) + 0.5f),
- static_cast(static_cast(j) * (std::sqrt(3) / 2.0)));
+ map.emplace_back(dimensions, (j % 2 == 0 ? static_cast(i) : static_cast(i) + 0.5f), j);
+ // static_cast(static_cast(j) * (std::sqrt(3) / 2.0))
break;
}
}
diff --git a/include/assign3/functions.h b/include/assign3/functions.h
index 1c6bd52..468a1e6 100644
--- a/include/assign3/functions.h
+++ b/include/assign3/functions.h
@@ -77,6 +77,23 @@ namespace assign3
blt::i32 width, height;
};
+ struct axial_distance_function_t : public distance_function_t
+ {
+ [[nodiscard]] Scalar distance(blt::span x, blt::span y) const final;
+ };
+
+ struct toroidal_axial_distance_function_t : public distance_function_t
+ {
+ public:
+ toroidal_axial_distance_function_t(blt::i32 width, blt::i32 height): width(width), height(height)
+ {}
+
+ [[nodiscard]] Scalar distance(blt::span x, blt::span y) const final;
+
+ private:
+ blt::i32 width, height;
+ };
+
}
#endif //COSC_4P80_ASSIGNMENT_3_FUNCTIONS_H
diff --git a/include/assign3/manager.h b/include/assign3/manager.h
index 176c9bc..5669ec9 100644
--- a/include/assign3/manager.h
+++ b/include/assign3/manager.h
@@ -82,10 +82,6 @@ namespace assign3
void cleanup();
- std::vector get_neuron_activations(const data_file_t& file);
-
- static std::vector normalize_data(const std::vector& data);
-
void draw_som(neuron_render_info_t info, const std::function& color_func);
void draw_debug(const data_file_t& file);
@@ -97,19 +93,22 @@ namespace assign3
switch (static_cast(selected_som_mode))
{
case shape_t::GRID:
- case shape_t::GRID_OFFSET:
distance_function = std::make_unique();
break;
- case shape_t::GRID_OFFSET_WRAP:
case shape_t::GRID_WRAP:
distance_function = std::make_unique(som_width, som_height);
break;
+ case shape_t::GRID_OFFSET:
+ distance_function = std::make_unique();
+ break;
+ case shape_t::GRID_OFFSET_WRAP:
+ distance_function = std::make_unique(som_width, som_height);
+ break;
}
- error_plotting.clear();
som = std::make_unique(motor_data.files[currently_selected_network], som_width, som_height, max_epochs,
- distance_function.get(), static_cast(selected_som_mode),
+ distance_function.get(), topology_function.get(), static_cast(selected_som_mode),
static_cast(selected_init_type), normalize_init);
- error_plotting.push_back(som->topological_error(motor_data.files[currently_selected_network]));
+ som->compute_neuron_activations();
}
private:
@@ -118,8 +117,6 @@ namespace assign3
std::unique_ptr topology_function;
std::unique_ptr distance_function;
- std::vector error_plotting;
-
blt::gfx::font_renderer_t fr2d{};
blt::gfx::batch_renderer_2d br2d;
diff --git a/include/assign3/som.h b/include/assign3/som.h
index 909957b..9e1327c 100644
--- a/include/assign3/som.h
+++ b/include/assign3/som.h
@@ -30,17 +30,23 @@ namespace assign3
{
public:
som_t(const data_file_t& file, blt::size_t width, blt::size_t height, blt::size_t max_epochs, distance_function_t* dist_func,
- shape_t shape, init_t init, bool normalize);
+ topology_function_t* topology_function, shape_t shape, init_t init, bool normalize);
blt::size_t get_closest_neuron(const std::vector& data);
Scalar find_closest_neighbour_distance(blt::size_t v0);
- void train_epoch(Scalar initial_learn_rate, topology_function_t* basis_func);
+ void train_epoch(Scalar initial_learn_rate);
blt::vec2 get_topological_position(const std::vector& data);
- Scalar topological_error(const data_file_t& data);
+ Scalar topological_error();
+
+ void compute_neuron_activations(Scalar distance = 2, Scalar activation = 0.5);
+
+ void write_activations(std::ostream& out);
+
+ void write_topology_errors(std::ostream& out);
[[nodiscard]] const array_t& get_array() const
{ return array; }
@@ -50,6 +56,9 @@ namespace assign3
[[nodiscard]] blt::size_t get_max_epochs() const
{ return max_epochs; }
+
+ [[nodiscard]] const std::vector& get_topological_errors() const
+ { return topological_errors; }
private:
array_t array;
@@ -57,6 +66,9 @@ namespace assign3
blt::size_t current_epoch = 0;
blt::size_t max_epochs;
distance_function_t* dist_func;
+ topology_function_t* topology_function;
+
+ std::vector topological_errors;
};
}
diff --git a/src/functions.cpp b/src/functions.cpp
index 90a592c..486da5d 100644
--- a/src/functions.cpp
+++ b/src/functions.cpp
@@ -54,4 +54,38 @@ namespace assign3
Scalar y_min = std::min(y_diff, static_cast(height) - y_diff);
return std::sqrt(x_min * x_min + y_min * y_min);
}
+
+ Scalar axial_distance_function_t::distance(blt::span x, blt::span y) const
+ {
+ static thread_local std::vector distances;
+ distances.clear();
+ Scalar total = 0;
+ for (auto [q, r] : blt::in_pairs(x, y))
+ {
+ distances.push_back(std::abs(q - r));
+ total += distances.back();
+ }
+
+ Scalar min = distances.front();
+ for (auto v : distances)
+ min = std::min(min, v);
+
+ return total - min;
+ }
+
+ Scalar axial_distance(Scalar q1, Scalar r1, Scalar q2, Scalar r2) {
+ return (std::abs(q1 - q2) + std::abs(r1 - r2) + std::abs((q1 + r1) - (q2 + r2))) / 2;
+ }
+
+ Scalar toroidal_axial_distance_function_t::distance(blt::span x, blt::span y) const
+ {
+ BLT_ASSERT(x.size() == 2 && y.size() == 2);
+
+ Scalar x_diff = std::abs(x[0] - y[0]);
+ Scalar y_diff = std::abs(x[1] - y[1]);
+ Scalar x_min = std::min(x_diff, static_cast(width) - x_diff);
+ Scalar y_min = std::min(y_diff, static_cast(height) - y_diff);
+ Scalar total = x_min + y_min;
+ return total - std::min(x_min, y_min);
+ }
}
\ No newline at end of file
diff --git a/src/main (conflicted copy 2024-11-18 194147).cpp b/src/main (conflicted copy 2024-11-18 194147).cpp
new file mode 100644
index 0000000..c30f011
--- /dev/null
+++ b/src/main (conflicted copy 2024-11-18 194147).cpp
@@ -0,0 +1,125 @@
+#include
+#include
+#include
+#include "blt/gfx/renderer/resource_manager.h"
+#include "blt/gfx/renderer/camera.h"
+#include "implot.h"
+#include
+#include
+
+using namespace assign3;
+
+blt::gfx::matrix_state_manager global_matrices;
+blt::gfx::resource_manager resources;
+blt::gfx::first_person_camera_2d camera;
+assign3::motor_data_t data{};
+assign3::renderer_t renderer{data, resources, global_matrices};
+
+void init(const blt::gfx::window_data&)
+{
+ using namespace blt::gfx;
+ BLT_INFO("Hello World!");
+
+ global_matrices.create_internals();
+ resources.load_resources();
+ renderer.create();
+
+ ImPlot::CreateContext();
+}
+
+void update(const blt::gfx::window_data& window_data)
+{
+ using namespace blt::gfx;
+ global_matrices.update_perspectives(window_data.width, window_data.height, 90, 0.1, 2000);
+
+ camera.update();
+ camera.update_view(global_matrices);
+ global_matrices.update();
+
+ renderer.render();
+}
+
+void destroy(const blt::gfx::window_data&)
+{
+ global_matrices.cleanup();
+ resources.cleanup();
+ renderer.cleanup();
+ ImPlot::DestroyContext();
+ blt::gfx::cleanup();
+ BLT_INFO("Goodbye World!");
+}
+
+void load_data_files(const std::string& str)
+{
+ data.files = assign3::data_file_t::load_data_files_from_path(str);
+ for (auto& v : data.files)
+ v = v.normalize();
+ data.update();
+}
+
+void action_start_graphics(const std::vector& argv_vector)
+{
+ blt::arg_parse parser{};
+
+ parser.addArgument(blt::arg_builder{"--file", "-f"}
+ .setDefault("../data")
+ .setHelp("Path to data files").build());
+
+ auto args = parser.parse_args(argv_vector);
+
+ load_data_files(args.get("file"));
+
+ blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, destroy}.setSyncInterval(1).setMaximized(true));
+}
+
+void action_test(const std::vector& argv_vector)
+{
+ blt::arg_parse parser{};
+
+ parser.addArgument(blt::arg_builder{"--file", "-f"}
+ .setDefault("../data")
+ .setHelp("Path to data files").build());
+
+ auto args = parser.parse_args(argv_vector);
+
+ load_data_files(args.get("file"));
+
+
+}
+
+void action_convert(const std::vector& argv_vector)
+{
+
+}
+
+int main(int argc, const char** argv)
+{
+ std::vector argv_vector;
+ for (int i = 0; i < argc; i++)
+ argv_vector.emplace_back(argv[i]);
+
+ blt::arg_parse parser{};
+
+ parser.addArgument(blt::arg_builder{"action"}
+ .setAction(blt::arg_action_t::SUBCOMMAND)
+ .setHelp("Action to run. Can be: [graphics, test, convert]").build());
+
+ auto args = parser.parse_args(argv_vector);
+
+ if (!args.contains("action"))
+ {
+ BLT_ERROR("Please provide an action");
+ return 0;
+ }
+
+ argv_vector.erase(argv_vector.begin() + 1);
+
+ auto action = blt::string::toLowerCase(args.get("action"));
+ if (action == "graphics")
+ action_start_graphics(argv_vector);
+ else if (action == "test")
+ action_test(argv_vector);
+ else if (action == "convert")
+ action_convert(argv_vector);
+
+}
diff --git a/src/main.cpp b/src/main.cpp
index 7de4ab8..e54b2e8 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -49,18 +49,82 @@ void destroy(const blt::gfx::window_data&)
BLT_INFO("Goodbye World!");
}
-int main(int argc, const char** argv)
+void load_data_files(const std::string& str)
{
- blt::arg_parse parser{};
-
- parser.addArgument(blt::arg_builder{"--file", "-f"}.setDefault("../data").setHelp("Path to data files").build());
-
- auto args = parser.parse_args(argc, argv);
-
- data.files = assign3::data_file_t::load_data_files_from_path(args.get("file"));
+ data.files = assign3::data_file_t::load_data_files_from_path(str);
for (auto& v : data.files)
v = v.normalize();
data.update();
+}
+
+void action_start_graphics(const std::vector& argv_vector)
+{
+ blt::arg_parse parser{};
+ parser.setHelpExtras("graphics");
+
+ parser.addArgument(blt::arg_builder{"--file", "-f"}
+ .setDefault("../data")
+ .setHelp("Path to data files").build());
+
+ auto args = parser.parse_args(argv_vector);
+
+ load_data_files(args.get("file"));
blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, destroy}.setSyncInterval(1).setMaximized(true));
}
+
+void action_test(const std::vector& argv_vector)
+{
+ blt::arg_parse parser{};
+ parser.setHelpExtras("test");
+
+ parser.addArgument(blt::arg_builder{"--file", "-f"}
+ .setDefault("../data")
+ .setHelp("Path to data files").build());
+
+ auto args = parser.parse_args(argv_vector);
+
+ load_data_files(args.get("file"));
+
+
+}
+
+void action_convert(const std::vector& argv_vector)
+{
+ blt::arg_parse parser{};
+ parser.setHelpExtras("convert");
+
+ auto args = parser.parse_args(argv_vector);
+}
+
+int main(int argc, const char** argv)
+{
+ std::vector argv_vector;
+ for (int i = 0; i < argc; i++)
+ argv_vector.emplace_back(argv[i]);
+
+ blt::arg_parse parser{};
+
+ parser.addArgument(blt::arg_builder{"action"}
+ .setAction(blt::arg_action_t::SUBCOMMAND)
+ .setHelp("Action to run. Can be: [graphics, test, convert]").build());
+
+ auto args = parser.parse_args(argv_vector);
+
+ if (!args.contains("action"))
+ {
+ BLT_ERROR("Please provide an action");
+ return 0;
+ }
+
+// argv_vector.erase(argv_vector.begin() + 1);
+
+ auto action = blt::string::toLowerCase(args.get("action"));
+ if (action == "graphics")
+ action_start_graphics(argv_vector);
+ else if (action == "test")
+ action_test(argv_vector);
+ else if (action == "convert")
+ action_convert(argv_vector);
+
+}
diff --git a/src/manager.cpp b/src/manager.cpp
index 3848d01..fe56908 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -123,8 +123,8 @@ namespace assign3
{
using namespace blt::gfx;
- ImGui::ShowDemoWindow();
- ImPlot::ShowDemoWindow();
+// ImGui::ShowDemoWindow();
+// ImPlot::ShowDemoWindow();
if (ImGui::Begin("Controls"))
{
@@ -137,7 +137,10 @@ namespace assign3
regenerate_network();
if (ImGui::Button("Run Epoch"))
- som->train_epoch(initial_learn_rate, topology_function.get());
+ {
+ som->train_epoch(initial_learn_rate);
+ som->compute_neuron_activations();
+ }
ImGui::Checkbox("Run to completion", &running);
ImGui::Text("Epoch %ld / %ld", som->get_current_epoch(), som->get_max_epochs());
}
@@ -152,8 +155,9 @@ namespace assign3
if (ImGui::ListBox("##InitType", &selected_init_type, get_selection_string, init_names.data(), static_cast(init_names.size())))
regenerate_network();
ImGui::TextWrapped("Help: %s", init_helps[selected_init_type].c_str());
- ImGui::Separator();
- ImGui::Checkbox("Normalize Init Data", &normalize_init);
+ if (ImGui::Checkbox("Normalize Init Data", &normalize_init))
+ regenerate_network();
+ ImGui::SeparatorText("Som Specifics");
if (ImGui::InputInt("SOM Width", &som_width) || ImGui::InputInt("SOM Height", &som_height) ||
ImGui::InputInt("Max Epochs", &max_epochs))
regenerate_network();
@@ -187,7 +191,11 @@ namespace assign3
static std::vector names;
names.clear();
for (blt::size_t i = 0; i < som->get_array().get_map().size(); i++)
- names.push_back("Neuron " + std::to_string(i));
+ {
+ auto pos = som->get_array().from_index(i);
+ names.push_back("Neuron " + std::to_string(i) +
+ " (" + std::to_string(pos.x()) + ", " + std::to_string(pos.y()) + ")");
+ }
ImGui::Text("Select Neuron");
ImGui::ListBox("##SelectNeuron", &selected_neuron, get_selection_string, names.data(), static_cast(names.size()));
}
@@ -199,14 +207,17 @@ namespace assign3
ImGui::End();
auto current_data_file = motor_data.files[currently_selected_network];
- auto closest_type = get_neuron_activations(current_data_file);
if (ImGui::Begin("Plots & Data"))
{
ImPlot::SetNextAxesLimits(0, som_width, 0, som_height, ImPlotCond_Always);
if (ImPlot::BeginPlot("Activations", ImVec2(-1, 0), ImPlotFlags_NoInputs))
{
- auto rev = rotate90Clockwise(closest_type, som_width, som_height);
+ static std::vector activations;
+ activations.clear();
+ for (const auto& n : som->get_array().get_map())
+ activations.push_back(n.get_activation());
+ auto rev = rotate90Clockwise(activations, som_width, som_height);
// auto rev = closest_type;
// std::reverse(rev.begin(), rev.end());
ImPlot::PlotHeatmap("##data_map", rev.data(), som_height, som_width, 0, 0, "%.1f", ImPlotPoint(0, 0),
@@ -216,7 +227,7 @@ namespace assign3
ImPlot::SetNextAxesLimits(0, max_epochs, 0, 1, ImPlotCond_Once);
if (ImPlot::BeginPlot("Error"))
{
- ImPlot::PlotLine("##error", error_plotting.data(), static_cast(error_plotting.size()));
+ ImPlot::PlotLine("##error", som->get_topological_errors().data(), static_cast(som->get_topological_errors().size()));
ImPlot::EndPlot();
}
}
@@ -226,18 +237,17 @@ namespace assign3
{
if (som->get_current_epoch() < som->get_max_epochs())
{
- som->train_epoch(initial_learn_rate, topology_function.get());
- error_plotting.push_back(som->topological_error(current_data_file));
+ som->train_epoch(initial_learn_rate);
+ som->compute_neuron_activations();
}
}
if (!debug_mode)
{
- closest_type = normalize_data(closest_type);
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];
+ [](render_data_t context) {
+ auto type = context.neuron.get_activation();
return type >= 0 ? blt::make_color(0, type, 0) : blt::make_color(-type, 0, 0);
});
} else
@@ -247,33 +257,6 @@ namespace assign3
fr2d.render();
}
- 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());
-
-
- for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
- {
- auto half = som->find_closest_neighbour_distance(i) / at_distance_measurement;
-// auto sigma = std::sqrt(-(half * half) / (2 * std::log(requested_activation)));
-// auto r = 1 / (2 * sigma * sigma);
-//
- auto scale = topology_function->scale(half, requested_activation);
- for (const auto& data : file.data_points)
- {
- auto ds = topology_function->call(v.dist(data.bins), scale);
- if (data.is_bad)
- closest_type[i] -= ds;
- else
- closest_type[i] += ds;
- }
- }
-
- return closest_type;
- }
-
void renderer_t::draw_debug(const data_file_t& file)
{
switch (static_cast(debug_state))
@@ -307,9 +290,8 @@ namespace assign3
}
const auto& data_point = file.data_points[selected_data_point];
- auto closest_type = normalize_data(get_neuron_activations(file));
draw_som(neuron_render_info_t{}.set_base_pos({370, 145}).set_neuron_scale(120).set_neuron_padding({0, 0}),
- [this, &neuron_positions, &data_point, &closest_type](render_data_t context) {
+ [this, &neuron_positions, &data_point](render_data_t context) {
auto half = som->find_closest_neighbour_distance(context.index) / at_distance_measurement;
auto scale = topology_function->scale(half, requested_activation);
auto ds = topology_function->call(context.neuron.dist(data_point.bins), scale);
@@ -321,7 +303,7 @@ namespace assign3
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];
+ auto type = context.neuron.get_activation();
return type >= 0 ? blt::make_color(0, type, 0) : blt::make_color(-type, 0, 0);
});
@@ -334,7 +316,6 @@ namespace assign3
break;
case debug_t::DISTANCE:
{
- auto closest_type = normalize_data(get_neuron_activations(file));
auto& selected_neuron_ref = som->get_array().get_map()[selected_neuron];
static std::vector distances_2d;
static std::vector distances_nd;
@@ -348,7 +329,7 @@ namespace assign3
}
draw_som(neuron_render_info_t{}.set_base_pos({370, 145}).set_neuron_scale(120).set_neuron_padding({0, 0}),
- [this, &closest_type](render_data_t context) {
+ [this](render_data_t context) {
auto& text = fr2d.render_text(
"2D: " + std::to_string(distances_2d[context.index]) + "\nND: " +
std::to_string(distances_nd[context.index]), 18).setColor(0.2, 0.2, 0.8);
@@ -357,24 +338,11 @@ namespace assign3
if (static_cast(selected_neuron) == context.index)
return blt::make_color(0, 0, 1);
- auto type = closest_type[context.index];
+ auto type = context.neuron.get_activation();
return type >= 0 ? blt::make_color(0, type, 0) : blt::make_color(-type, 0, 0);
});
}
break;
}
}
-
- std::vector renderer_t::normalize_data(const std::vector& data)
- {
- std::vector return_data;
- auto min_act = *std::min_element(data.begin(), data.end());
- auto max_act = *std::max_element(data.begin(), data.end());
- for (auto& v : data)
- {
- auto n = 2 * (v - min_act) / (max_act - min_act) - 1;
- return_data.push_back(n);
- }
- return return_data;
- }
}
\ No newline at end of file
diff --git a/src/som.cpp b/src/som.cpp
index b8b0ec4..c37db04 100644
--- a/src/som.cpp
+++ b/src/som.cpp
@@ -27,14 +27,16 @@ namespace assign3
{
som_t::som_t(const data_file_t& file, blt::size_t width, blt::size_t height, blt::size_t max_epochs, distance_function_t* dist_func,
- shape_t shape, init_t init, bool normalize):
- array(file.data_points.begin()->bins.size(), width, height, shape), file(file), max_epochs(max_epochs), dist_func(dist_func)
+ topology_function_t* topology_function, shape_t shape, init_t init, bool normalize):
+ array(file.data_points.begin()->bins.size(), width, height, shape), file(file), max_epochs(max_epochs), dist_func(dist_func),
+ topology_function(topology_function)
{
for (auto& v : array.get_map())
v.randomize(std::random_device{}(), init, normalize, file);
+ topological_errors.push_back(topological_error());
}
- void som_t::train_epoch(Scalar initial_learn_rate, topology_function_t* basis_func)
+ void som_t::train_epoch(Scalar initial_learn_rate)
{
blt::random::random_t rand{std::random_device{}()};
std::shuffle(file.data_points.begin(), file.data_points.end(), rand);
@@ -52,18 +54,18 @@ namespace assign3
auto distance_min = find_closest_neighbour_distance(v0_idx);
// this will find the required scaling factor to make a point in the middle between v0 and its closest neighbour activate 50%
// from the perspective of the gaussian function
- auto scale = basis_func->scale(distance_min * 0.5f, 0.5);
+ auto scale = topology_function->scale(distance_min * 0.5f, 0.5);
for (auto [i, n] : blt::enumerate(array.get_map()))
{
if (i == v0_idx)
continue;
- auto dist = basis_func->call(neuron_t::distance(dist_func, v0, n), time_ratio * scale);
+ auto dist = topology_function->call(neuron_t::distance(dist_func, v0, n), time_ratio * scale);
n.update(current_data.bins, dist, eta);
}
}
-
current_epoch++;
+ topological_errors.push_back(topological_error());
}
blt::size_t som_t::get_closest_neuron(const std::vector& data)
@@ -139,12 +141,12 @@ namespace assign3
return (dp1 * p_1) + (dp2 * p_2) + (dp3 * p_3);
}
- Scalar som_t::topological_error(const data_file_t& data)
+ Scalar som_t::topological_error()
{
Scalar total = 0;
std::vector> distances;
- for (const auto& x : data.data_points)
+ for (const auto& x : file.data_points)
{
distances.clear();
for (const auto& [i, n] : blt::enumerate(array.get_map()))
@@ -171,7 +173,53 @@ namespace assign3
total += 1;
}
- return total / static_cast(data.data_points.size());
+ return total / static_cast(file.data_points.size());
+ }
+
+ void som_t::compute_neuron_activations(Scalar distance, Scalar activation)
+ {
+ for (auto& n : array.get_map())
+ n.set_activation(0);
+
+ Scalar min = std::numeric_limits::max();
+ Scalar max = std::numeric_limits::min();
+
+ for (auto [i, v] : blt::enumerate(array.get_map()))
+ {
+ auto half = find_closest_neighbour_distance(i) / distance;
+// auto sigma = std::sqrt(-(half * half) / (2 * std::log(requested_activation)));
+// auto r = 1 / (2 * sigma * sigma);
+//
+ auto scale = topology_function->scale(half, activation);
+ for (const auto& data : file.data_points)
+ {
+ auto ds = topology_function->call(v.dist(data.bins), scale);
+ if (data.is_bad)
+ v.activate(-ds);
+ else
+ v.activate(ds);
+ }
+
+ min = std::min(min, v.get_activation());
+ max = std::max(max, v.get_activation());
+ }
+
+ for (auto& n : array.get_map())
+ n.set_activation(2 * (n.get_activation() - min) / (max - min) - 1);
+ }
+
+ void som_t::write_activations(std::ostream& out)
+ {
+ out << "x,y,activation\n";
+ for (const auto& v : array.get_map())
+ out << v.get_x() << ',' << v.get_y() << ',' << v.get_activation() << '\n';
+ }
+
+ void som_t::write_topology_errors(std::ostream& out)
+ {
+ out << "epoch,error\n";
+ for (auto [i, v] : blt::enumerate(topological_errors))
+ out << i << ',' << v << '\n';
}