update python commit file for nix + changes to som. working now?

main
Brett 2024-11-10 15:37:10 -05:00
parent 3b13e48f36
commit e589a07531
10 changed files with 146 additions and 26 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.9) project(COSC-4P80-Assignment-3 VERSION 0.0.10)
include(FetchContent) include(FetchContent)
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)

View File

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!python3
import subprocess import subprocess
import argparse import argparse

View File

@ -36,28 +36,33 @@ namespace assign3
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);
} }
inline neuron_t& get(blt::size_t x, blt::size_t y) [[nodiscard]] std::pair<blt::size_t, blt::size_t> from_index(blt::size_t index) const
{
return {index % width, index / width};
}
neuron_t& get(blt::size_t x, blt::size_t y)
{ return map[y * width + x]; } { return map[y * width + x]; }
[[nodiscard]] inline const neuron_t& get(blt::size_t x, blt::size_t y) const [[nodiscard]] const neuron_t& get(blt::size_t x, blt::size_t y) const
{ return map[y * width + x]; } { return map[y * width + x]; }
[[nodiscard]] inline blt::size_t get_width() const [[nodiscard]] blt::size_t get_width() const
{ return width; } { return width; }
[[nodiscard]] inline blt::size_t get_height() const [[nodiscard]] blt::size_t get_height() const
{ return height; } { return height; }
[[nodiscard]] inline std::vector<neuron_t>& get_map() [[nodiscard]] std::vector<neuron_t>& get_map()
{ return map; } { return map; }
[[nodiscard]] inline const std::vector<neuron_t>& get_map() const [[nodiscard]] const std::vector<neuron_t>& get_map() const
{ return map; } { return map; }
private: private:
[[nodiscard]] inline blt::i64 wrap_width(blt::i64 x) const; [[nodiscard]] blt::i64 wrap_width(blt::i64 x) const;
[[nodiscard]] inline blt::i64 wrap_height(blt::i64 y) const; [[nodiscard]] blt::i64 wrap_height(blt::i64 y) const;
private: private:
blt::i64 width, height; blt::i64 width, height;

View File

@ -33,16 +33,31 @@ namespace assign3
* @return basis results * @return basis results
*/ */
[[nodiscard]] virtual Scalar call(Scalar dist, Scalar r) const = 0; [[nodiscard]] virtual Scalar call(Scalar dist, Scalar r) const = 0;
/**
* produces a non-time scaled r value for making the function activate at target_strength when a point is half_distance between two nodes
* so for the example of unit distance 1, half distance is 0.5, target_strength is 0.5
* this function will produce a value which can be time scaled for achieving this result at t=0
*
* this function can return a 1 to indicate this function doesn't care for this.
*/
[[nodiscard]] virtual Scalar scale(Scalar half_distance, Scalar target_strength) const = 0;
virtual ~topology_function_t() = default;
}; };
struct gaussian_function_t : public topology_function_t struct gaussian_function_t final : public topology_function_t
{ {
[[nodiscard]] Scalar call(Scalar dist, Scalar r) const final; [[nodiscard]] Scalar call(Scalar dist, Scalar r) const final;
[[nodiscard]] Scalar scale(Scalar half_distance, Scalar target_strength) const final;
}; };
struct distance_function_t struct distance_function_t
{ {
[[nodiscard]] virtual Scalar distance(blt::span<const Scalar> x, blt::span<const Scalar> y) const = 0; [[nodiscard]] virtual Scalar distance(blt::span<const Scalar> x, blt::span<const Scalar> y) const = 0;
virtual ~distance_function_t() = default;
}; };
struct euclidean_distance_function_t : public distance_function_t struct euclidean_distance_function_t : public distance_function_t

View File

@ -19,13 +19,24 @@
#ifndef COSC_4P80_ASSIGNMENT_3_FWDECL_H #ifndef COSC_4P80_ASSIGNMENT_3_FWDECL_H
#define COSC_4P80_ASSIGNMENT_3_FWDECL_H #define COSC_4P80_ASSIGNMENT_3_FWDECL_H
#include <blt/std/types.h>
namespace assign3 namespace assign3
{ {
using Scalar = float; using Scalar = float;
enum class shape inline constexpr blt::i32 RENDER_2D = 0x0;
inline constexpr blt::i32 RENDER_3D = 0x1;
enum class shape : blt::i32
{ {
GRID, HONEYCOMB GRID = RENDER_2D,
GRID_WRAP = RENDER_2D,
GRID_OFFSET = RENDER_2D,
GRID_OFFSET_WRAP = RENDER_2D,
GAUSSIAN_DIST = RENDER_2D,
TOROIDAL = RENDER_3D,
CYLINDER = RENDER_3D
}; };
} }

View File

@ -33,6 +33,8 @@ namespace assign3
blt::size_t get_closest_neuron(const std::vector<Scalar>& data); blt::size_t get_closest_neuron(const std::vector<Scalar>& 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, topology_function_t* basis_func);
[[nodiscard]] const array_t& get_array() const [[nodiscard]] const array_t& get_array() const

View File

@ -28,6 +28,11 @@ namespace assign3
return std::exp(-r * dist_sq); return std::exp(-r * dist_sq);
} }
Scalar gaussian_function_t::scale(Scalar half_distance, Scalar target_strength) const
{
return -std::log(target_strength) / (half_distance * half_distance);
}
Scalar euclidean_distance_function_t::distance(blt::span<const Scalar> x, blt::span<const Scalar> y) const Scalar euclidean_distance_function_t::distance(blt::span<const Scalar> x, blt::span<const Scalar> y) const
{ {
Scalar dist = 0; Scalar dist = 0;

View File

@ -8,6 +8,7 @@
#include <assign3/file.h> #include <assign3/file.h>
#include <assign3/som.h> #include <assign3/som.h>
#include <imgui.h> #include <imgui.h>
#include <execution>
using namespace assign3; using namespace assign3;
@ -19,17 +20,38 @@ blt::gfx::resource_manager resources;
blt::gfx::batch_renderer_2d renderer_2d(resources, global_matrices); blt::gfx::batch_renderer_2d renderer_2d(resources, global_matrices);
blt::gfx::first_person_camera_2d camera; blt::gfx::first_person_camera_2d camera;
blt::size_t som_width = 7; blt::size_t som_width = 5;
blt::size_t som_height = 7; blt::size_t som_height = 5;
blt::size_t max_epochs = 1000; blt::size_t max_epochs = 100;
Scalar initial_learn_rate = 0.1; Scalar initial_learn_rate = 0.1;
int currently_selected_network = 0; int currently_selected_network = 0;
std::vector<std::string> map_files_names; std::vector<std::string> map_files_names;
std::unique_ptr<topology_function_t> topology_function;
float neuron_scale = 35; float neuron_scale = 35;
float draw_width = neuron_scale * static_cast<float>(som_width); float draw_width;
float draw_height = neuron_scale * static_cast<float>(som_height); 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<gaussian_function_t>();
}
void generate_network(int selection) void generate_network(int selection)
{ {
@ -65,6 +87,8 @@ void update(const blt::gfx::window_data& window_data)
camera.update_view(global_matrices); camera.update_view(global_matrices);
global_matrices.update(); global_matrices.update();
update_graphics();
if (ImGui::Begin("Controls")) if (ImGui::Begin("Controls"))
{ {
ImGui::Text("Network Select"); ImGui::Text("Network Select");
@ -73,16 +97,14 @@ void update(const blt::gfx::window_data& window_data)
if (ImGui::Button("Run Epoch")) if (ImGui::Button("Run Epoch"))
{ {
static gaussian_function_t func; som->train_epoch(initial_learn_rate, topology_function.get());
som->train_epoch(initial_learn_rate, &func);
} }
static bool run; static bool run;
ImGui::Checkbox("Run to completion", &run); ImGui::Checkbox("Run to completion", &run);
if (run) if (run)
{ {
static gaussian_function_t func;
if (som->get_current_epoch() < som->get_max_epochs()) if (som->get_current_epoch() < som->get_max_epochs())
som->train_epoch(initial_learn_rate, &func); som->train_epoch(initial_learn_rate, topology_function.get());
} }
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());
} }
@ -156,6 +178,47 @@ void update(const blt::gfx::window_data& window_data)
v.get_y() * neuron_scale + 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});
}
renderer_2d.render(window_data.width, window_data.height); renderer_2d.render(window_data.width, window_data.height);
} }
@ -178,5 +241,5 @@ int main(int argc, const char** argv)
files = assign3::data_file_t::load_data_files_from_path(args.get<std::string>("file")); files = assign3::data_file_t::load_data_files_from_path(args.get<std::string>("file"));
blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, destroy}.setSyncInterval(1)); blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, destroy}.setSyncInterval(1).setMaximized(true));
} }

View File

@ -33,6 +33,7 @@ namespace assign3
return *this; return *this;
} }
// apply the distance based on the update neuron function
neuron_t& neuron_t::update(const std::vector<Scalar>& new_data, Scalar dist, Scalar eta) neuron_t& neuron_t::update(const std::vector<Scalar>& new_data, Scalar dist, Scalar eta)
{ {
static thread_local std::vector<Scalar> diff; static thread_local std::vector<Scalar> diff;
@ -47,12 +48,14 @@ namespace assign3
return *this; return *this;
} }
// distance between an input vector and the neuron, in the n-space
Scalar neuron_t::dist(const std::vector<Scalar>& X) const Scalar neuron_t::dist(const std::vector<Scalar>& X) const
{ {
euclidean_distance_function_t dist_func; euclidean_distance_function_t dist_func;
return dist_func.distance(data, X); return dist_func.distance(data, X);
} }
// distance between two neurons, in 2d
Scalar neuron_t::distance(const neuron_t& n1, const neuron_t& n2) Scalar neuron_t::distance(const neuron_t& n1, const neuron_t& n2)
{ {
euclidean_distance_function_t dist_func; euclidean_distance_function_t dist_func;

View File

@ -21,6 +21,7 @@
#include <blt/std/random.h> #include <blt/std/random.h>
#include <blt/iterator/enumerate.h> #include <blt/iterator/enumerate.h>
#include <blt/std/logging.h> #include <blt/std/logging.h>
#include <cstring>
namespace assign3 namespace assign3
{ {
@ -46,11 +47,17 @@ namespace assign3
auto v0 = array.get_map()[v0_idx]; auto v0 = array.get_map()[v0_idx];
v0.update(current_data.bins, 1, eta); v0.update(current_data.bins, 1, eta);
// find the closest neighbour neuron to v0
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);
for (auto [i, n] : blt::enumerate(array.get_map())) for (auto [i, n] : blt::enumerate(array.get_map()))
{ {
if (i == v0_idx) if (i == v0_idx)
continue; continue;
auto dist = basis_func->call(neuron_t::distance(v0, n), time_ratio); auto dist = basis_func->call(neuron_t::distance(v0, n), time_ratio * scale);
n.update(current_data.bins, dist, eta); n.update(current_data.bins, dist, eta);
} }
} }
@ -62,7 +69,6 @@ namespace assign3
{ {
blt::size_t index = 0; blt::size_t index = 0;
Scalar distance = std::numeric_limits<Scalar>::max(); Scalar distance = std::numeric_limits<Scalar>::max();
for (auto [i, d] : blt::enumerate(array.get_map())) for (auto [i, d] : blt::enumerate(array.get_map()))
{ {
auto dist = d.dist(data); auto dist = d.dist(data);
@ -72,9 +78,19 @@ namespace assign3
distance = dist; distance = dist;
} }
} }
return index; return index;
} }
Scalar som_t::find_closest_neighbour_distance(blt::size_t v0)
{
Scalar distance_min = std::numeric_limits<Scalar>::max();
for (const auto& [i, n] : blt::enumerate(array.get_map()))
{
if (i != v0)
distance_min = std::min(distance_min, neuron_t::distance(array.get_map()[v0], n));
}
return distance_min;
}
} }