kohonen_som_trace.cpp File Reference
Machine Learning Algorithms

Kohonen self organizing map (data tracing) More...

`#include <algorithm>`
`#include <array>`
`#include <cmath>`
`#include <cstdlib>`
`#include <ctime>`
`#include <fstream>`
`#include <iostream>`
`#include <valarray>`
`#include <vector>`
Include dependency graph for kohonen_som_trace.cpp:

## Namespaces

| machine_learning
| Machine learning algorithms.

## Functions

double | _random (double a, double b)
int | save_nd_data (const char *fname, const std::vector< std::valarray< double >> &X)
void | machine_learning::update_weights (const std::valarray< double > &x, std::vector< std::valarray< double >> *W, std::valarray< double > *D, double alpha, int R)
void | machine_learning::kohonen_som_tracer (const std::vector< std::valarray< double >> &X, std::vector< std::valarray< double >> *W, double alpha_min)
void | test_circle (std::vector< std::valarray< double >> *data)
void | test1 ()
void | test_lamniscate (std::vector< std::valarray< double >> *data)
void | test2 ()
void | test_3d_classes (std::vector< std::valarray< double >> *data)
void | test3 ()
double | get_clock_diff (clock_t start_t, clock_t end_t)
int | main (int argc, char **argv)

## Detailed Description

Kohonen self organizing map (data tracing)

This example implements a powerful self organizing map algorithm. The algorithm creates a connected network of weights that closely follows the given data points. This this creates a chain of nodes that resembles the given input shape.

Author
  Krishna Vedala

Note
  This C++ version of the program is considerable slower than its C counterpart
  The compiled code is much slower when compiled with MS Visual C++ 2019 than with GCC on windows

See also
  kohonen_som_topology.cpp

## Function Documentation

## get_clock_diff()

double get_clock_diff | ( | clock_t | start_t,
|  | clock_t | end_t
| ) |  |

Convert clock cycle difference to time in seconds

Parameters

  [in] | start_t | start clock
  [in] | end_t | end clock

Returns
  time difference in seconds

{
return static_cast<double>(end_t - start_t) / CLOCKS_PER_SEC;
}

## main()

int main | ( | int | argc,
|  | char ** | argv
| ) |  |

Main function

{
#ifdef _OPENMP
std::cout << "Using OpenMP based parallelization\n";
#else
std::cout << "NOT using OpenMP based parallelization\n";
#endif
std::srand(std::time(nullptr));
std::clock_t start_clk = std::clock();
test1();
auto end_clk = std::clock();
std::cout << "Test 1 completed in " << get_clock_diff(start_clk, end_clk)
<< " sec\n";
start_clk = std::clock();
test2();
end_clk = std::clock();
std::cout << "Test 2 completed in " << get_clock_diff(start_clk, end_clk)
<< " sec\n";
start_clk = std::clock();
test3();
end_clk = std::clock();
std::cout << "Test 3 completed in " << get_clock_diff(start_clk, end_clk)
<< " sec\n";
std::cout
<< "(Note: Calculated times include: creating test sets, training "
"model and writing files to disk.)\n\n";
return 0;
}
std::cout
std::clock
T clock(T... args)
std::clock_t
test2
void test2()
Definition: kohonen_som_trace.cpp:315
test1
void test1()
Definition: kohonen_som_trace.cpp:233
get_clock_diff
double get_clock_diff(clock_t start_t, clock_t end_t)
Definition: kohonen_som_trace.cpp:452
test3
void test3()
Definition: kohonen_som_trace.cpp:414
std::srand
T srand(T... args)
std::time
T time(T... args)
Here is the call graph for this function:

## test1()

void test1 | ( |  | ) |

Test that creates a random set of points distributed near the circumference of a circle and trains an SOM that finds that circular pattern. The following CSV files are created to validate the execution:

- `test1.csv`: random test samples points with a circular pattern
- `w11.csv`: initial random map
- `w12.csv`: trained SOM map

The outputs can be readily plotted in gnuplot using the following snippet

set datafile separator ','
plot "test1.csv" title "original", \
"w11.csv" title "w1", \
"w12.csv" title "w2"

{
int j = 0, N = 500;
int features = 2;
int num_out = 50;
std::vector<std::valarray<double>> X(N);
std::vector<std::valarray<double>> W(num_out);
for (int i = 0; i < std::max(num_out, N); i++) {
// loop till max(N, num_out)
if (i < N) { // only add new arrays if i < N
X[i] = std::valarray<double>(features);
}
if (i < num_out) { // only add new arrays if i < num_out
W[i] = std::valarray<double>(features);
#ifdef _OPENMP
#pragma omp for
#endif
for (j = 0; j < features; j++) {
// preallocate with random initial weights
W[i][j] = _random(-1, 1);
}
}
}
test_circle(&X); // create test data around circumference of a circle
save_nd_data("test1.csv", X); // save test data points
save_nd_data("w11.csv", W); // save initial random weights
kohonen_som_tracer(X, &W, 0.1); // train the SOM
save_nd_data("w12.csv", W); // save the resultant weights
}
save_nd_data
int save_nd_data(const char *fname, const std::vector< std::valarray< double >> &X)
Definition: kohonen_som_trace.cpp:58
_random
double _random(double a, double b)
Definition: kohonen_som_topology.cpp:53
test_circle
void test_circle(std::vector< std::valarray< double >> *data)
Definition: kohonen_som_trace.cpp:196
std::max
T max(T... args)
machine_learning::kohonen_som_tracer
void kohonen_som_tracer(const std::vector< std::valarray< double >> &X, std::vector< std::valarray< double >> *W, double alpha_min)
Definition: kohonen_som_trace.cpp:149
std::valarray< double >
std::vector
Here is the call graph for this function:

## test2()

void test2 | ( |  | ) |

Test that creates a random set of points distributed near the locus of the Lamniscate of Gerono and trains an SOM that finds that circular pattern. The following CSV files are created to validate the execution:

- `test2.csv`: random test samples points with a lamniscate pattern
- `w21.csv`: initial random map
- `w22.csv`: trained SOM map

The outputs can be readily plotted in gnuplot using the following snippet

set datafile separator ','
plot "test2.csv" title "original", \
"w21.csv" title "w1", \
"w22.csv" title "w2"

{
int j = 0, N = 500;
int features = 2;
int num_out = 20;
std::vector<std::valarray<double>> X(N);
std::vector<std::valarray<double>> W(num_out);
for (int i = 0; i < std::max(num_out, N); i++) {
// loop till max(N, num_out)
if (i < N) { // only add new arrays if i < N
X[i] = std::valarray<double>(features);
}
if (i < num_out) { // only add new arrays if i < num_out
W[i] = std::valarray<double>(features);
#ifdef _OPENMP
#pragma omp for
#endif
for (j = 0; j < features; j++) {
// preallocate with random initial weights
W[i][j] = _random(-1, 1);
}
}
}
test_lamniscate(&X); // create test data around the lamniscate
save_nd_data("test2.csv", X); // save test data points
save_nd_data("w21.csv", W); // save initial random weights
kohonen_som_tracer(X, &W, 0.01); // train the SOM
save_nd_data("w22.csv", W); // save the resultant weights
}
test_lamniscate
void test_lamniscate(std::vector< std::valarray< double >> *data)
Definition: kohonen_som_trace.cpp:277
Here is the call graph for this function:

## test3()

void test3 | ( |  | ) |

Test that creates a random set of points distributed in six clusters in 3D space. The following CSV files are created to validate the execution:

- `test3.csv`: random test samples points with a circular pattern
- `w31.csv`: initial random map
- `w32.csv`: trained SOM map

The outputs can be readily plotted in gnuplot using the following snippet

set datafile separator ','
plot "test3.csv" title "original", \
"w31.csv" title "w1", \
"w32.csv" title "w2"

{
int j = 0, N = 200;
int features = 3;
int num_out = 20;
std::vector<std::valarray<double>> X(N);
std::vector<std::valarray<double>> W(num_out);
for (int i = 0; i < std::max(num_out, N); i++) {
// loop till max(N, num_out)
if (i < N) { // only add new arrays if i < N
X[i] = std::valarray<double>(features);
}
if (i < num_out) { // only add new arrays if i < num_out
W[i] = std::valarray<double>(features);
#ifdef _OPENMP
#pragma omp for
#endif
for (j = 0; j < features; j++) {
// preallocate with random initial weights
W[i][j] = _random(-1, 1);
}
}
}
test_3d_classes(&X); // create test data around the lamniscate
save_nd_data("test3.csv", X); // save test data points
save_nd_data("w31.csv", W); // save initial random weights
kohonen_som_tracer(X, &W, 0.01); // train the SOM
save_nd_data("w32.csv", W); // save the resultant weights
}
test_3d_classes
void test_3d_classes(std::vector< std::valarray< double >> *data)
Definition: kohonen_som_trace.cpp:359
Here is the call graph for this function:

## test_3d_classes()

void test_3d_classes | ( | std::vector< std::valarray< double >> * | data | ) |

Creates a random set of points distributed in six clusters in 3D space with centroids at the points

- \({0.5, 0.5, 0.5}\)
- \({0.5, 0.5, -0.5}\)
- \({0.5, -0.5, 0.5}\)
- \({0.5, -0.5, -0.5}\)
- \({-0.5, 0.5, 0.5}\)
- \({-0.5, 0.5, -0.5}\)
- \({-0.5, -0.5, 0.5}\)
- \({-0.5, -0.5, -0.5}\)

Parameters

  [out] | data | matrix to store data in

{
const int N = data->size();
const double R = 0.1; // radius of cluster
int i = 0;
const int num_classes = 8;
const std::array<const std::array<double, 3>, num_classes> centres = {
// centres of each class cluster
std::array<double, 3>({.5, .5, .5}), // centre of class 0
std::array<double, 3>({.5, .5, -.5}), // centre of class 1
std::array<double, 3>({.5, -.5, .5}), // centre of class 2
std::array<double, 3>({.5, -.5, -.5}), // centre of class 3
std::array<double, 3>({-.5, .5, .5}), // centre of class 4
std::array<double, 3>({-.5, .5, -.5}), // centre of class 5
std::array<double, 3>({-.5, -.5, .5}), // centre of class 6
std::array<double, 3>({-.5, -.5, -.5}) // centre of class 7
};
#ifdef _OPENMP
#pragma omp for
#endif
for (i = 0; i < N; i++) {
int cls =
std::rand() % num_classes; // select a random class for the point
// create random coordinates (x,y,z) around the centre of the class
data[0][i][0] = _random(centres[cls][0] - R, centres[cls][0] + R);
data[0][i][1] = _random(centres[cls][1] - R, centres[cls][1] + R);
data[0][i][2] = _random(centres[cls][2] - R, centres[cls][2] + R);
/* The follosing can also be used
for (int j = 0; j < 3; j++)
data[0][i][j] = _random(centres[cls][j] - R, centres[cls][j] + R);
*/
}
}
std::array
data
int data[MAX]
test data
Definition: hash_search.cpp:24
std::rand
T rand(T... args)
Here is the call graph for this function:

## test_circle()

void test_circle | ( | std::vector< std::valarray< double >> * | data | ) |

Creates a random set of points distributed near the circumference of a circle and trains an SOM that finds that circular pattern. The generating function is

\begin{eqnarray*} r &\in& [1-\delta r, 1+\delta r)\\ \theta &\in& [0, 2\pi)\\ x &=& r\cos\theta\\ y &=& r\sin\theta \end{eqnarray*}

Parameters

  [out] | data | matrix to store data in

{
const int N = data->size();
const double R = 0.75, dr = 0.3;
double a_t = 0., b_t = 2.f * M_PI; // theta random between 0 and 2*pi
double a_r = R - dr, b_r = R + dr; // radius random between R-dr and R+dr
int i = 0;
#ifdef _OPENMP
#pragma omp for
#endif
for (i = 0; i < N; i++) {
double r = _random(a_r, b_r); // random radius
double theta = _random(a_t, b_t); // random theta
data[0][i][0] = r * cos(theta); // convert from polar to cartesian
data[0][i][1] = r * sin(theta);
}
}
std::cos
T cos(T... args)
std::sin
T sin(T... args)
Here is the call graph for this function:

## test_lamniscate()

void test_lamniscate | ( | std::vector< std::valarray< double >> * | data | ) |

Creates a random set of points distributed near the locus of the Lamniscate of Gerono.

\begin{eqnarray*} \delta r &=& 0.2\\ \delta x &\in& [-\delta r, \delta r)\\ \delta y &\in& [-\delta r, \delta r)\\ \theta &\in& [0, \pi)\\ x &=& \delta x + \cos\theta\\ y &=& \delta y + \frac{\sin(2\theta)}{2} \end{eqnarray*}

Parameters

  [out] | data | matrix to store data in

{
const int N = data->size();
const double dr = 0.2;
int i = 0;
#ifdef _OPENMP
#pragma omp for
#endif
for (i = 0; i < N; i++) {
double dx = _random(-dr, dr); // random change in x
double dy = _random(-dr, dr); // random change in y
double theta = _random(0, M_PI); // random theta
data[0][i][0] = dx + cos(theta); // convert from polar to cartesian
data[0][i][1] = dy + sin(2. * theta) / 2.f;
}
}
Here is the call graph for this function: