diff --git a/.idea/workspace (conflicted copy 2024-11-18 173055).xml b/.idea/workspace (conflicted copy 2024-11-18 173055).xml
deleted file mode 100644
index 7688f0a..0000000
--- a/.idea/workspace (conflicted copy 2024-11-18 173055).xml
+++ /dev/null
@@ -1,222 +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 ba280ad..b94938c 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.21)
+project(COSC-4P80-Assignment-3 VERSION 0.0.22)
include(FetchContent)
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
@@ -14,6 +14,12 @@ FetchContent_Declare(implot
FIND_PACKAGE_ARGS)
FetchContent_MakeAvailable(implot)
+FetchContent_Declare(matplotplusplus
+ GIT_REPOSITORY https://github.com/alandefreitas/matplotplusplus
+ GIT_TAG v1.2.1
+ FIND_PACKAGE_ARGS)
+FetchContent_MakeAvailable(matplotplusplus)
+
add_subdirectory(lib/blt-with-graphics)
include_directories(include/)
@@ -26,7 +32,7 @@ add_executable(COSC-4P80-Assignment-3 ${PROJECT_BUILD_FILES} ${IM_PLOT_FILES})
target_compile_options(COSC-4P80-Assignment-3 PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
target_link_options(COSC-4P80-Assignment-3 PRIVATE -Wall -Wextra -Wpedantic -Wno-comment)
-target_link_libraries(COSC-4P80-Assignment-3 PRIVATE BLT_WITH_GRAPHICS)
+target_link_libraries(COSC-4P80-Assignment-3 PRIVATE BLT_WITH_GRAPHICS matplot)
if (${ENABLE_ADDRSAN} MATCHES ON)
target_compile_options(COSC-4P80-Assignment-3 PRIVATE -fsanitize=address)
diff --git a/include/assign3/functions.h b/include/assign3/functions.h
index 468a1e6..ff6f1de 100644
--- a/include/assign3/functions.h
+++ b/include/assign3/functions.h
@@ -21,6 +21,7 @@
#include
#include
+#include
namespace assign3
{
@@ -58,6 +59,8 @@ namespace assign3
[[nodiscard]] virtual Scalar distance(blt::span x, blt::span y) const = 0;
virtual ~distance_function_t() = default;
+
+ static std::unique_ptr from_shape(shape_t shape, blt::u32 som_width, blt::u32 som_height);
};
struct euclidean_distance_function_t : public distance_function_t
diff --git a/include/assign3/manager.h b/include/assign3/manager.h
index 5669ec9..cd398de 100644
--- a/include/assign3/manager.h
+++ b/include/assign3/manager.h
@@ -90,21 +90,7 @@ namespace assign3
void regenerate_network()
{
- switch (static_cast(selected_som_mode))
- {
- case shape_t::GRID:
- distance_function = std::make_unique();
- break;
- 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;
- }
+ distance_function = distance_function_t::from_shape(static_cast(selected_som_mode), som_width, som_height);
som = std::make_unique(motor_data.files[currently_selected_network], som_width, som_height, max_epochs,
distance_function.get(), topology_function.get(), static_cast(selected_som_mode),
static_cast(selected_init_type), normalize_init);
diff --git a/include/assign3/som.h b/include/assign3/som.h
index 9e1327c..dca8871 100644
--- a/include/assign3/som.h
+++ b/include/assign3/som.h
@@ -42,12 +42,18 @@ namespace assign3
Scalar topological_error();
+ Scalar quantization_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);
+ void write_quantization_errors(std::ostream& out);
+
+ void write_all_errors(std::ostream& out);
+
[[nodiscard]] const array_t& get_array() const
{ return array; }
@@ -59,6 +65,9 @@ namespace assign3
[[nodiscard]] const std::vector& get_topological_errors() const
{ return topological_errors; }
+
+ [[nodiscard]] const std::vector& get_quantization_errors() const
+ { return quantization_errors; }
private:
array_t array;
@@ -68,7 +77,11 @@ namespace assign3
distance_function_t* dist_func;
topology_function_t* topology_function;
+ // normalized value for which below this will be considered neural
+ float quantization_distance = 0.25;
+
std::vector topological_errors;
+ std::vector quantization_errors;
};
}
diff --git a/src/functions.cpp b/src/functions.cpp
index 486da5d..82fef2b 100644
--- a/src/functions.cpp
+++ b/src/functions.cpp
@@ -88,4 +88,20 @@ namespace assign3
Scalar total = x_min + y_min;
return total - std::min(x_min, y_min);
}
+
+ std::unique_ptr distance_function_t::from_shape(shape_t shape, blt::u32 som_width, blt::u32 som_height)
+ {
+ switch (shape)
+ {
+ case shape_t::GRID:
+ return std::make_unique();
+ case shape_t::GRID_WRAP:
+ return std::make_unique(som_width, som_height);
+ case shape_t::GRID_OFFSET:
+ return std::make_unique();
+ case shape_t::GRID_OFFSET_WRAP:
+ return std::make_unique(som_width, som_height);
+ }
+ return nullptr;
+ }
}
\ 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
deleted file mode 100644
index c30f011..0000000
--- a/src/main (conflicted copy 2024-11-18 194147).cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-#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 e54b2e8..13187c4 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -6,6 +6,11 @@
#include "implot.h"
#include
#include
+#include
+#include
+#include
+#include
+#include
using namespace assign3;
@@ -73,6 +78,32 @@ void action_start_graphics(const std::vector& argv_vector)
blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, destroy}.setSyncInterval(1).setMaximized(true));
}
+struct activation
+{
+ Scalar x, y, act;
+};
+
+struct task_t // NOLINT
+{
+ data_file_t* file;
+ blt::u32 width, height;
+ blt::size_t max_epochs;
+ shape_t shape;
+ init_t init;
+ Scalar initial_learn_rate;
+
+ task_t() = default; // NOLINT
+
+ task_t(data_file_t* file, blt::u32 width, blt::u32 height, size_t maxEpochs, shape_t shape, init_t init, Scalar initial_learn_rate):
+ file(file), width(width), height(height), max_epochs(maxEpochs), shape(shape), init(init), initial_learn_rate(initial_learn_rate)
+ {}
+
+ gaussian_function_t topology_func{};
+ std::vector> topological_errors{};
+ std::vector> quantization_errors{};
+ std::vector> activations{};
+};
+
void action_test(const std::vector& argv_vector)
{
blt::arg_parse parser{};
@@ -86,7 +117,112 @@ void action_test(const std::vector& argv_vector)
load_data_files(args.get("file"));
+ std::vector tasks;
+ std::vector threads;
+ std::mutex task_mutex;
+ tasks.emplace_back(&data.files[1], 5, 5, 1000, shape_t::GRID, init_t::RANDOM_DATA, 0.1);
+
+ static blt::size_t runs = 30;
+
+ for (blt::size_t i = 0; i < std::thread::hardware_concurrency(); i++)
+ {
+ threads.emplace_back([&task_mutex, &tasks]() {
+ do
+ {
+ task_t task;
+ {
+ std::scoped_lock lock(task_mutex);
+ if (tasks.empty())
+ break;
+ task = std::move(tasks.back());
+ tasks.pop_back();
+ }
+
+ for (blt::size_t i = 0; i < runs; i++)
+ {
+ gaussian_function_t func{};
+ auto dist = distance_function_t::from_shape(task.shape, task.width, task.height);
+ std::unique_ptr som = std::make_unique(*task.file, task.width, task.height, task.max_epochs, dist.get(),
+ &task.topology_func, task.shape, task.init, false);
+ while (som->get_current_epoch() < som->get_max_epochs())
+ som->train_epoch(task.initial_learn_rate);
+ som->compute_neuron_activations();
+
+ task.topological_errors.push_back(som->get_topological_errors());
+ task.quantization_errors.push_back(som->get_quantization_errors());
+ std::vector acts;
+ for (const auto& neuron : som->get_array().get_map())
+ acts.push_back({neuron.get_x(), neuron.get_y(), neuron.get_activation()});
+ task.activations.emplace_back(std::move(acts));
+ }
+ std::stringstream paths;
+ paths << "./bins-" << task.file->data_points.begin()->bins.size() << "/";
+ paths << task.width << "x" << task.height << '-' << task.max_epochs << '/';
+ std::string shape_name = shape_names[static_cast(task.shape)];
+ blt::string::replaceAll(shape_name, " ", "-");
+ paths << shape_name << '/';
+ std::string init_name = init_names[static_cast(task.init)];
+ blt::string::replaceAll(init_name, " ", "-");
+ paths << init_name << '-' << task.initial_learn_rate << '/';
+ auto path = paths.str();
+ std::filesystem::create_directories(path);
+
+ std::vector average_topological_errors;
+ std::vector average_quantization_errors;
+ std::vector average_activations;
+
+ average_topological_errors.resize(task.topological_errors.begin()->size());
+ average_quantization_errors.resize(task.quantization_errors.begin()->size());
+ average_activations.resize(task.activations.begin()->size());
+
+ for (const auto& vec : task.topological_errors)
+ for (auto [index, v] : blt::enumerate(vec))
+ average_topological_errors[index] += v;
+ for (const auto& vec : task.quantization_errors)
+ for (auto [index, v] : blt::enumerate(vec))
+ average_quantization_errors[index] += v;
+ for (const auto& vec : task.activations)
+ for (auto [index, v] : blt::enumerate(vec))
+ average_activations[index].act += v.act;
+
+ for (auto& v : average_topological_errors)
+ v /= static_cast(runs);
+ for (auto& v : average_quantization_errors)
+ v /= static_cast(runs);
+ for (auto& v : average_activations)
+ v.act /= static_cast(runs);
+
+ auto f = matplot::figure();
+ f->tiledlayout(2, 1);
+ auto axis = f->add_axes();
+ axis->hold(true);
+ axis->plot(matplot::linspace(0, static_cast(task.max_epochs)), average_topological_errors)->display_name("Topological Error");
+ axis->title("Error");
+ axis->xlabel("Epoch");
+ axis->ylabel("Error");
+ axis->grid(true);
+ axis->plot(matplot::linspace(0, static_cast(task.max_epochs)), average_quantization_errors)->display_name("Quantization Error");
+ f->title("Topological and Quantization Errors, " + std::to_string(runs) + " Runs");
+
+ f->save((path + "errors_plot.eps"), "postscript");
+ f->save((path + "errors_plot.tex"), "epslatex");
+ f->save((path + "errors_plot.png"), "png");
+
+ BLT_INFO("Task '%s' Complete", path.c_str());
+
+ } while (true);
+ });
+ }
+
+ while (!threads.empty())
+ {
+ if (threads.back().joinable())
+ {
+ threads.back().join();
+ threads.pop_back();
+ }
+ }
}
void action_convert(const std::vector& argv_vector)
@@ -116,7 +252,7 @@ int main(int argc, const char** argv)
BLT_ERROR("Please provide an action");
return 0;
}
-
+
// argv_vector.erase(argv_vector.begin() + 1);
auto action = blt::string::toLowerCase(args.get("action"));
diff --git a/src/manager.cpp b/src/manager.cpp
index fe56908..9f4b974 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -225,9 +225,15 @@ namespace assign3
ImPlot::EndPlot();
}
ImPlot::SetNextAxesLimits(0, max_epochs, 0, 1, ImPlotCond_Once);
- if (ImPlot::BeginPlot("Error"))
+ if (ImPlot::BeginPlot("TError"))
{
- ImPlot::PlotLine("##error", som->get_topological_errors().data(), static_cast(som->get_topological_errors().size()));
+ ImPlot::PlotLine("##Terror", som->get_topological_errors().data(), static_cast(som->get_topological_errors().size()));
+ ImPlot::EndPlot();
+ }
+ ImPlot::SetNextAxesLimits(0, max_epochs, 0, static_cast(current_data_file.data_points.size()), ImPlotCond_Once);
+ if (ImPlot::BeginPlot("QError"))
+ {
+ ImPlot::PlotLine("##Qerror", som->get_quantization_errors().data(), static_cast(som->get_quantization_errors().size()));
ImPlot::EndPlot();
}
}
diff --git a/src/neuron.cpp b/src/neuron.cpp
index 67c5cf2..bf3ffe5 100644
--- a/src/neuron.cpp
+++ b/src/neuron.cpp
@@ -34,7 +34,7 @@ namespace assign3
break;
case init_t::RANDOM_DATA:
{
- static std::vector min_values, max_values;
+ static thread_local std::vector min_values, max_values;
min_values.clear();
max_values.clear();
diff --git a/src/som.cpp b/src/som.cpp
index c37db04..a6a33f7 100644
--- a/src/som.cpp
+++ b/src/som.cpp
@@ -22,6 +22,7 @@
#include
#include
#include
+#include "blt/iterator/zip.h"
namespace assign3
{
@@ -34,6 +35,7 @@ namespace assign3
for (auto& v : array.get_map())
v.randomize(std::random_device{}(), init, normalize, file);
topological_errors.push_back(topological_error());
+ quantization_errors.push_back(quantization_error());
}
void som_t::train_epoch(Scalar initial_learn_rate)
@@ -66,6 +68,7 @@ namespace assign3
}
current_epoch++;
topological_errors.push_back(topological_error());
+ quantization_errors.push_back(quantization_error());
}
blt::size_t som_t::get_closest_neuron(const std::vector& data)
@@ -222,5 +225,49 @@ namespace assign3
out << i << ',' << v << '\n';
}
+ void som_t::write_quantization_errors(std::ostream& out)
+ {
+ out << "epoch,error\n";
+ for (auto [i, v] : blt::enumerate(quantization_errors))
+ out << i << ',' << v << '\n';
+ }
+
+ void som_t::write_all_errors(std::ostream& out)
+ {
+ out << "epoch,topology error,quantization error\n";
+ for (auto [i, v] : blt::in_pairs(topological_errors, quantization_errors).enumerate())
+ {
+ auto [t, q] = v;
+ out << i << ',' << t << ',' << q << '\n';
+ }
+ }
+
+ Scalar som_t::quantization_error()
+ {
+ Scalar incorrect = 0;
+
+ for (const auto& point : file.data_points)
+ {
+ const auto& nearest = array.get_map()[get_closest_neuron(point.bins)];
+
+ bool is_neural = nearest.get_activation() > -quantization_distance && nearest.get_activation() < quantization_distance;
+
+ if (is_neural)
+ {
+ incorrect++;
+ continue;
+ }
+
+ bool is_bad = nearest.get_activation() <= -quantization_distance;
+ bool is_good = nearest.get_activation() >= quantization_distance;
+
+ if ((is_bad && point.is_bad) || (is_good && !point.is_bad))
+ continue;
+ incorrect++;
+ }
+
+ return incorrect;
+ }
+
}
\ No newline at end of file