grid don't work

main
Brett 2024-11-18 00:53:32 -05:00
parent 45ccb3647e
commit 1e3ed0755a
8 changed files with 129 additions and 53 deletions

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.25) cmake_minimum_required(VERSION 3.25)
project(COSC-4P80-Assignment-3 VERSION 0.0.18) project(COSC-4P80-Assignment-3 VERSION 0.0.19)
include(FetchContent) include(FetchContent)
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)

View File

@ -29,12 +29,24 @@ namespace assign3
class array_t class array_t
{ {
public: public:
explicit array_t(blt::size_t dimensions, blt::size_t width, blt::size_t height): explicit array_t(blt::size_t dimensions, blt::size_t width, blt::size_t height, shape_t shape):
width(static_cast<blt::i64>(width)), height(static_cast<blt::i64>(height)) width(static_cast<blt::i64>(width)), height(static_cast<blt::i64>(height))
{ {
for (blt::size_t i = 0; i < width; i++) switch (shape)
{
case shape_t::GRID:
case shape_t::GRID_WRAP:
for (blt::size_t j = 0; j < height; j++) for (blt::size_t j = 0; j < height; j++)
for (blt::size_t i = 0; i < width; i++)
map.emplace_back(dimensions, i, j);
break;
case shape_t::GRID_OFFSET:
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<Scalar>(i) : static_cast<Scalar>(i) + 0.5f), j); map.emplace_back(dimensions, (j % 2 == 0 ? static_cast<Scalar>(i) : static_cast<Scalar>(i) + 0.5f), j);
break;
}
} }
[[nodiscard]] blt::vec2ul from_index(blt::size_t index) const [[nodiscard]] blt::vec2ul from_index(blt::size_t index) const

View File

@ -30,15 +30,19 @@ namespace assign3
inline constexpr blt::i32 RENDER_2D = 0x0; inline constexpr blt::i32 RENDER_2D = 0x0;
inline constexpr blt::i32 RENDER_3D = 0x1; inline constexpr blt::i32 RENDER_3D = 0x1;
enum class shape : blt::i32 enum class shape_t : blt::i32
{ {
GRID = RENDER_2D, GRID,
GRID_WRAP = RENDER_2D, GRID_WRAP,
GRID_OFFSET = RENDER_2D, GRID_OFFSET,
GRID_OFFSET_WRAP = RENDER_2D, GRID_OFFSET_WRAP
GAUSSIAN_DIST = RENDER_2D, };
TOROIDAL = RENDER_3D,
CYLINDER = RENDER_3D inline std::array<std::string, 4> shape_names{
"Grid",
"Edge Wrapped Grid",
"Honey Comb Grid",
"Edge Wrapped Honey Comb"
}; };
enum class debug_type enum class debug_type

View File

@ -92,31 +92,43 @@ namespace assign3
void render(); void render();
void update_graphics();
void regenerate_network() void regenerate_network()
{ {
som = std::make_unique<som_t>(motor_data.files[currently_selected_network].normalize(), som_width, som_height, max_epochs); switch (static_cast<shape_t>(selected_som_mode))
{
case shape_t::GRID:
case shape_t::GRID_OFFSET:
distance_function = std::make_unique<euclidean_distance_function_t>();
break;
case shape_t::GRID_OFFSET_WRAP:
case shape_t::GRID_WRAP:
distance_function = std::make_unique<toroidal_euclidean_distance_function_t>(som_width, som_height);
break;
}
error_plotting.clear();
som = std::make_unique<som_t>(motor_data.files[currently_selected_network], som_width, som_height, max_epochs,
distance_function.get(), static_cast<shape_t>(selected_som_mode));
error_plotting.push_back(som->topological_error(motor_data.files[currently_selected_network]));
} }
private: private:
motor_data_t& motor_data; motor_data_t& motor_data;
std::unique_ptr<som_t> som; std::unique_ptr<som_t> som;
std::unique_ptr<topology_function_t> topology_function; std::unique_ptr<topology_function_t> topology_function;
std::unique_ptr<distance_function_t> distance_function;
std::vector<Scalar> error_plotting;
blt::gfx::font_renderer_t fr2d{}; blt::gfx::font_renderer_t fr2d{};
blt::gfx::batch_renderer_2d br2d; blt::gfx::batch_renderer_2d br2d;
float draw_width = 0;
float draw_height = 0;
float neuron_scale = 35;
blt::i32 som_width = 5; blt::i32 som_width = 5;
blt::i32 som_height = 5; blt::i32 som_height = 5;
blt::i32 max_epochs = 100; blt::i32 max_epochs = 2000;
Scalar initial_learn_rate = 0.1; Scalar initial_learn_rate = 1;
int currently_selected_network = 0; int currently_selected_network = 0;
int selected_som_mode = 0;
bool debug_mode = false; bool debug_mode = false;
bool draw_colors = true; bool draw_colors = true;
bool draw_data_lines = false; bool draw_data_lines = false;

View File

@ -42,17 +42,30 @@ namespace assign3
[[nodiscard]] Scalar dist(const std::vector<Scalar>& X) const; [[nodiscard]] Scalar dist(const std::vector<Scalar>& X) const;
[[nodiscard]] inline const std::vector<Scalar>& get_data() const neuron_t& set_activation(Scalar act)
{
activation = act;
return *this;
}
void activate(Scalar act)
{ activation += act; }
[[nodiscard]] const std::vector<Scalar>& get_data() const
{ return data; } { return data; }
[[nodiscard]] inline Scalar get_x() const [[nodiscard]] Scalar get_x() const
{ return x_pos; } { return x_pos; }
[[nodiscard]] inline Scalar get_y() const [[nodiscard]] Scalar get_y() const
{ return y_pos; } { return y_pos; }
[[nodiscard]] Scalar get_activation() const
{ return activation; }
private: private:
Scalar x_pos, y_pos; Scalar x_pos, y_pos;
Scalar activation = 0;
std::vector<Scalar> data; std::vector<Scalar> data;
}; };

View File

@ -29,7 +29,7 @@ namespace assign3
class som_t class som_t
{ {
public: public:
som_t(const data_file_t& file, blt::size_t width, blt::size_t height, blt::size_t max_epochs); 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);
blt::size_t get_closest_neuron(const std::vector<Scalar>& data); blt::size_t get_closest_neuron(const std::vector<Scalar>& data);
@ -39,6 +39,8 @@ namespace assign3
blt::vec2 get_topological_position(const std::vector<Scalar>& data); blt::vec2 get_topological_position(const std::vector<Scalar>& data);
Scalar topological_error(const data_file_t& data);
[[nodiscard]] const array_t& get_array() const [[nodiscard]] const array_t& get_array() const
{ return array; } { return array; }
@ -53,6 +55,7 @@ namespace assign3
data_file_t file; data_file_t file;
blt::size_t current_epoch = 0; blt::size_t current_epoch = 0;
blt::size_t max_epochs; blt::size_t max_epochs;
distance_function_t* dist_func;
}; };
} }

View File

@ -97,7 +97,6 @@ namespace assign3
topology_function = std::make_unique<gaussian_function_t>(); topology_function = std::make_unique<gaussian_function_t>();
regenerate_network(); regenerate_network();
update_graphics();
} }
void renderer_t::cleanup() void renderer_t::cleanup()
@ -142,8 +141,13 @@ namespace assign3
ImGui::Checkbox("Run to completion", &running); ImGui::Checkbox("Run to completion", &running);
ImGui::Text("Epoch %ld / %ld", som->get_current_epoch(), som->get_max_epochs()); ImGui::Text("Epoch %ld / %ld", som->get_current_epoch(), som->get_max_epochs());
} }
ImGui::SetNextItemOpen(true, ImGuiCond_Appearing);
if (ImGui::CollapsingHeader("SOM Settings")) if (ImGui::CollapsingHeader("SOM Settings"))
{ {
ImGui::Text("Network Shape");
if (ImGui::ListBox("##NetworkShape", &selected_som_mode, get_selection_string, shape_names.data(),
static_cast<int>(shape_names.size())))
regenerate_network();
if (ImGui::InputInt("SOM Width", &som_width) || ImGui::InputInt("SOM Height", &som_height) || if (ImGui::InputInt("SOM Width", &som_width) || ImGui::InputInt("SOM Height", &som_height) ||
ImGui::InputInt("Max Epochs", &max_epochs)) ImGui::InputInt("Max Epochs", &max_epochs))
regenerate_network(); regenerate_network();
@ -195,13 +199,22 @@ namespace assign3
ImPlotPoint(som_width, som_height), ImPlotHeatmapFlags_ColMajor); ImPlotPoint(som_width, som_height), ImPlotHeatmapFlags_ColMajor);
ImPlot::EndPlot(); ImPlot::EndPlot();
} }
ImPlot::SetNextAxesLimits(0, max_epochs, 0, 1, ImPlotCond_Once);
if (ImPlot::BeginPlot("Error"))
{
ImPlot::PlotLine("##error", error_plotting.data(), static_cast<int>(error_plotting.size()));
ImPlot::EndPlot();
}
} }
ImGui::End(); ImGui::End();
if (running) if (running)
{ {
if (som->get_current_epoch() < som->get_max_epochs()) if (som->get_current_epoch() < som->get_max_epochs())
{
som->train_epoch(initial_learn_rate, topology_function.get()); som->train_epoch(initial_learn_rate, topology_function.get());
error_plotting.push_back(som->topological_error(current_data_file));
}
} }
@ -220,24 +233,6 @@ namespace assign3
fr2d.render(); 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);
draw_height = (max_y - min_y);
}
std::vector<float> renderer_t::get_neuron_activations(const data_file_t& file) std::vector<float> renderer_t::get_neuron_activations(const data_file_t& file)
{ {
static std::vector<float> closest_type; static std::vector<float> closest_type;
@ -248,6 +243,9 @@ namespace assign3
for (auto [i, v] : blt::enumerate(som->get_array().get_map())) for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
{ {
auto half = som->find_closest_neighbour_distance(i) / at_distance_measurement; 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); auto scale = topology_function->scale(half, requested_activation);
for (const auto& data : file.data_points) for (const auto& data : file.data_points)
{ {

View File

@ -26,8 +26,9 @@
namespace assign3 namespace assign3
{ {
som_t::som_t(const data_file_t& file, blt::size_t width, blt::size_t height, blt::size_t max_epochs): 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,
array(file.data_points.begin()->bins.size(), width, height), file(file), max_epochs(max_epochs) shape_t shape):
array(file.data_points.begin()->bins.size(), width, height, shape), file(file), max_epochs(max_epochs), dist_func(dist_func)
{ {
for (auto& v : array.get_map()) for (auto& v : array.get_map())
v.randomize(std::random_device{}()); v.randomize(std::random_device{}());
@ -57,8 +58,7 @@ namespace assign3
{ {
if (i == v0_idx) if (i == v0_idx)
continue; continue;
toroidal_euclidean_distance_function_t dist_func{static_cast<blt::i32>(array.get_width()), static_cast<blt::i32>(array.get_height())}; auto dist = basis_func->call(neuron_t::distance(dist_func, v0, n), time_ratio * scale);
auto dist = basis_func->call(neuron_t::distance(&dist_func, v0, n), time_ratio * scale);
n.update(current_data.bins, dist, eta); n.update(current_data.bins, dist, eta);
} }
} }
@ -84,12 +84,11 @@ namespace assign3
Scalar som_t::find_closest_neighbour_distance(blt::size_t v0) Scalar som_t::find_closest_neighbour_distance(blt::size_t v0)
{ {
toroidal_euclidean_distance_function_t dist_func{static_cast<blt::i32>(array.get_width()), static_cast<blt::i32>(array.get_height())};
Scalar distance_min = std::numeric_limits<Scalar>::max(); Scalar distance_min = std::numeric_limits<Scalar>::max();
for (const auto& [i, n] : blt::enumerate(array.get_map())) for (const auto& [i, n] : blt::enumerate(array.get_map()))
{ {
if (i != v0) if (i != v0)
distance_min = std::min(distance_min, neuron_t::distance(&dist_func, array.get_map()[v0], n)); distance_min = std::min(distance_min, neuron_t::distance(dist_func, array.get_map()[v0], n));
} }
return distance_min; return distance_min;
} }
@ -140,5 +139,40 @@ namespace assign3
return (dp1 * p_1) + (dp2 * p_2) + (dp3 * p_3); return (dp1 * p_1) + (dp2 * p_2) + (dp3 * p_3);
} }
Scalar som_t::topological_error(const data_file_t& data)
{
Scalar total = 0;
std::vector<std::pair<blt::size_t, Scalar>> distances;
for (const auto& x : data.data_points)
{
distances.clear();
for (const auto& [i, n] : blt::enumerate(array.get_map()))
distances.emplace_back(i, n.dist(x.bins));
std::pair<blt::size_t, Scalar> min1 = {0, std::numeric_limits<Scalar>::max()};
std::pair<blt::size_t, Scalar> min2 = {0, std::numeric_limits<Scalar>::max()};
for (const auto& elem : distances)
{
if (elem.second < min1.second)
{
min2 = min1;
min1 = elem;
} else if (elem.second < min2.second)
min2 = elem;
}
// we can assert the neurons are neighbours if the distance between the BMUs and the nearest neighbour are equal.
auto min_distances = neuron_t::distance(dist_func, array.get_map()[min1.first], array.get_map()[min2.first]);
auto neighbour_distances = find_closest_neighbour_distance(min1.first);
if (!blt::f_equal(min_distances, neighbour_distances))
total += 1;
}
return total / static_cast<Scalar>(data.data_points.size());
}
} }