License : Creative Commons Attribution 4.0 International (CC BY-NC-SA 4.0)
Copyright : Hervé Frezza-Buet, CentraleSupelec
Last modified : March 28, 2024 13:08
Link to the source : builder.md

Table of contents

The CxSOM builder

The cxsom-builder tools enable to rationalize the definition of a multi-map architecture. Once you have understood the following, have a look at the “experiments” section of the package. It provides examples of realistic experiments with cxsom-builder that you can replay by following step by step instructions.

Principle

Timelines of a map

The maps in an architecture hosts variables belonging to different timelines. Indeed, all the variables in a timeline, for each instant, relax together in order to reach the consensus. If an update of a variable takes as argument variables in another timeline, it is not feasible if that variables are not computed beforehand. On the contrary, for the arguments in the same timeline at the same time instant, computation can start even if arguments are not computed with a final value yet… this is relaxation.

Each maps defined by cxsom-builder organize the computation in the following timelines.

Relations between maps

Each map can have multiple inputs, with a weight layer and a matching activity associated to it. There are two kinds of inputs:

Implementing a 1D SOM with cxsom-builder

This is performed by the following code.

#include <cxsom-builder.hpp>
#include <fstream>
 
#define CACHE       2
#define TRACE   10000
#define OPENED   true
#define FORGET      0
#define WALLTIME TRACE-1
 
#define MAP_SIZE 500
 
// cxsom declarations
using namespace cxsom::rules;
context* cxsom::rules::ctx = nullptr;
 
int main(int argc, char* argv[]) {
  context c(argc, argv);
  
  kwd::parameters p_main, p_match, p_learn, p_external, p_contextual, p_global;
  p_main       | kwd::use("walltime", WALLTIME),  kwd::use("epsilon", 0);
  p_match      | p_main, kwd::use("sigma", .1);
  p_learn      | p_main, kwd::use("alpha", .1), kwd::use("r", .1);
  p_external   | p_main;
  p_contextual | p_main;
  p_global     | p_main, kwd::use("random-bmu", 1);
 
  auto map_settings = cxsom::builder::map::make_settings();
  map_settings.map_size            = MAP_SIZE;
  map_settings.cache_size          = CACHE;
  map_settings.internals_file_size = FORGET;
  map_settings.weights_file_size   = TRACE;
  map_settings.bmu_file_size       = TRACE;
  map_settings.kept_opened         = OPENED;
  map_settings                     = {p_external, p_contextual, p_global};
  
  auto archi = cxsom::builder::architecture();
  
  auto input = cxsom::builder::variable("in", cxsom::builder::name("1D") / "xi", "Scalar", CACHE, FORGET, OPENED);
  input->definition(); // Adds the definition rule for that input.
  kwd::var(input->timeline, input->varname) << fx::random() | kwd::use("walltime", WALLTIME);
  
  auto map = cxsom::builder::map::make_1D("SOM");
  
  map->external(input, fx::match_gaussian, p_match, fx::learn_triangle, p_learn);
  
  archi << map;
  *archi = map_settings;
  archi->realize();
  
  map->internals_random_at(0);
  
  // We describe the architecture in a dot file.
  std::ofstream dot_file("som-archi.dot");
  dot_file << archi->write_dot;
  return 0;
}

This single map architecture is denoted by the following graph (generated by the C++ code, see the updates here))

The cxsom-builder architecture notation.
The cxsom-builder architecture notation.

Implementing a 1D recSOM with cxsom-builder

A recurrent SOM is a map that receives two external inputs. One is an input as for usual SOM, except that the order is relevant (i.e. inputs are items of a sequence). The second is tue output of the map, taken at previous time step. Here, that output is the BMU.

This is performed by the following code.

#include <cxsom-builder.hpp>
#include <fstream>
 
#define CACHE       2
#define TRACE   10000
#define OPENED   true
#define FORGET      0
#define WALLTIME   -1 // Infinite walltime
 
#define MAP_SIZE 500
 
// cxsom declarations
using namespace cxsom::rules;
context* cxsom::rules::ctx = nullptr;
 
int main(int argc, char* argv[]) {
  context c(argc, argv);
  
  auto archi = cxsom::builder::architecture();
  
  kwd::parameters p_main, p_match, p_learn, p_external, p_contextual, p_global;
  p_main       | kwd::use("walltime", WALLTIME), kwd::use("epsilon", 0);
  p_match      | p_main, kwd::use("r", .3);
  p_learn      | p_main, kwd::use("alpha", .05), kwd::use("r", .05);
  p_external   | p_main;
  p_contextual | p_main;
  p_global     | p_main, kwd::use("random-bmu", 1), kwd::use("sigma", .0125);

  auto map_settings = cxsom::builder::map::make_settings();
  map_settings.map_size          = MAP_SIZE;
  map_settings.cache_size        = CACHE;
  map_settings.weights_file_size = TRACE;
  map_settings.kept_opened       = OPENED;
  map_settings                   = {p_external, p_contextual, p_global};
  map_settings.bmu_file_size = TRACE; // Default is 0.
  
  auto input = cxsom::builder::variable("in", cxsom::builder::name("X"), "Scalar", CACHE, TRACE, OPENED);
  input->definition();
 
  auto map = cxsom::builder::map::make_1D("recSOM");
 
  map->external(input, fx::match_triangle, p_match,                                       fx::learn_triangle, p_learn);
  map->external(map,   fx::match_triangle, p_match, cxsom::builder::timestep::previous(), fx::learn_triangle, p_learn);
   
  archi << map;
  *archi = map_settings;
  archi->realize();
 
  map->internals_random_at(0);
  
  std::ofstream dot_file("recsom-archi.dot");
  dot_file << archi->write_dot;
  return 0;
}

This single map architecture is denoted by the following graph (generated by the C++ code, see the updates here)

The cxsom-builder architecture notation.
The cxsom-builder architecture notation.

Implementing a two relaxing SOMs with cxsom-builder

Here, we link two SOMs with a contextual relation. Their respective BMUs relax together in order to find a consensus.

This is performed by the following code.

#include <cxsom-builder.hpp>
#include <fstream>

#define CACHE       2
#define TRACE   10000
#define OPENED   true
#define FORGET      0
#define WALLTIME   -1 // Infinite walltime

#define MAP_SIZE 500

using namespace cxsom::rules;
context* cxsom::rules::ctx = nullptr;

int main(int argc, char* argv[]) {
  context c(argc, argv);

  auto archi = cxsom::builder::architecture();
  
  kwd::parameters p_main, p_match, p_learn, p_learn_e, p_learn_c, p_external, p_contextual, p_global;
  p_main       | kwd::use("walltime", WALLTIME), kwd::use("epsilon", 0);
  p_match      | p_main, kwd::use("sigma", .2);
  p_learn      | p_main, kwd::use("alpha", .05);
  p_learn_e    | p_learn, kwd::use("r", .25 );
  p_learn_c    | p_learn, kwd::use("r", .075);
  p_external   | p_main;
  p_contextual | p_main;
  p_global     | p_main, kwd::use("random-bmu", 1), kwd::use("sigma", .01), kwd::use("beta", .5), kwd::use("delta", .01), kwd::use("deadline", 100);
  
  auto map_settings = cxsom::builder::map::make_settings();
  map_settings.map_size          = MAP_SIZE;
  map_settings.cache_size        = CACHE;
  map_settings.weights_file_size = TRACE;
  map_settings.kept_opened       = OPENED;
  map_settings                   = {p_external, p_contextual, p_global};

  auto X = cxsom::builder::variable("in", cxsom::builder::name("X"), "Scalar", CACHE, TRACE, OPENED);
  auto Y = cxsom::builder::variable("in", cxsom::builder::name("Y"), "Scalar", CACHE, TRACE, OPENED);
  X->definition();
  Y->definition();

  auto Xmap = cxsom::builder::map::make_1D("X");
  auto Ymap = cxsom::builder::map::make_1D("Y");
  
  Xmap->external  (X,    fx::match_gaussian, p_match, fx::learn_triangle, p_learn_e);
  Ymap->external  (Y,    fx::match_gaussian, p_match, fx::learn_triangle, p_learn_e);
  Xmap->contextual(Ymap, fx::match_gaussian, p_match, fx::learn_triangle, p_learn_c);
  Ymap->contextual(Xmap, fx::match_gaussian, p_match, fx::learn_triangle, p_learn_c);

  archi << Xmap << Ymap;
  *archi = map_settings;
  archi->realize();
  Xmap->internals_random_at(0);
  Ymap->internals_random_at(0);
  
  std::ofstream dot_file("2soms-archi.dot");
  dot_file << archi->write_dot;  
  return 0;
}

This 2 map architecture is denoted by the following graph (generated by the C++ code, see the updates here)

The cxsom-builder architecture notation.
The cxsom-builder architecture notation.

More complex architectures…

With cxsom-builder, more complex architectures can be easily defined.

For example, the following code…

#include <cxsom-builder.hpp>
#include <fstream>

#define CACHE       2
#define TRACE   10000
#define OPENED   true
#define FORGET      0
#define WALLTIME   -1 // Infinite walltime

#define MAP_SIZE 500

using namespace cxsom::rules;
context* cxsom::rules::ctx = nullptr;


int main(int argc, char* argv[]) {
  context c(argc, argv);

  auto archi = cxsom::builder::architecture();
  
  kwd::parameters p_main, p_match, p_learn, p_learn_e, p_learn_c, p_external, p_contextual, p_global;
  p_main       | kwd::use("walltime", WALLTIME), kwd::use("epsilon", 0);
  p_match      | p_main, kwd::use("sigma", .2);
  p_learn      | p_main, kwd::use("alpha", .05);
  p_learn_e    | p_learn, kwd::use("r", .25 );
  p_learn_c    | p_learn, kwd::use("r", .075);
  p_external   | p_main;
  p_contextual | p_main;
  p_global     | p_main, kwd::use("random-bmu", 1), kwd::use("sigma", .01), kwd::use("beta", .5), kwd::use("delta", .01), kwd::use("deadline", 100);

  auto map_settings = cxsom::builder::map::make_settings();
  map_settings.map_size          = MAP_SIZE;
  map_settings.cache_size        = CACHE;
  map_settings.weights_file_size = TRACE;
  map_settings.kept_opened       = OPENED;
  map_settings                   = {p_external, p_contextual, p_global};

  auto X = cxsom::builder::variable("in", cxsom::builder::name("X"), "Scalar", CACHE, TRACE, OPENED);
  auto Y = cxsom::builder::variable("in", cxsom::builder::name("Y"), "Scalar", CACHE, TRACE, OPENED);
  auto Z = cxsom::builder::variable("in", cxsom::builder::name("Z"), "Scalar", CACHE, TRACE, OPENED);
  X->definition();
  Y->definition();
  Z->definition();

  auto Xmap = cxsom::builder::map::make_1D("X");
  auto Ymap = cxsom::builder::map::make_1D("Y");
  auto Zmap = cxsom::builder::map::make_1D("Z");
  auto Amap = cxsom::builder::map::make_1D("Assoc");
  
  Xmap->external  (X,    fx::match_gaussian, p_match, fx::learn_triangle, p_learn_e);
  Ymap->external  (Y,    fx::match_gaussian, p_match, fx::learn_triangle, p_learn_e);
  Zmap->external  (Z,    fx::match_gaussian, p_match, fx::learn_triangle, p_learn_e);
  Xmap->contextual(Amap, fx::match_gaussian, p_match, fx::learn_triangle, p_learn_c);
  Ymap->contextual(Amap, fx::match_gaussian, p_match, fx::learn_triangle, p_learn_c);
  Zmap->contextual(Amap, fx::match_gaussian, p_match, fx::learn_triangle, p_learn_c);
  Amap->contextual(Xmap, fx::match_gaussian, p_match, fx::learn_triangle, p_learn_c);
  Amap->contextual(Ymap, fx::match_gaussian, p_match, fx::learn_triangle, p_learn_c);
  Amap->contextual(Zmap, fx::match_gaussian, p_match, fx::learn_triangle, p_learn_c);
  
  Amap->external(Amap, fx::match_triangle, p_match, cxsom::builder::timestep::previous(), fx::learn_triangle, p_learn);

  archi << Xmap << Ymap << Zmap << Amap;
  *archi = map_settings;
  archi->realize();
  Xmap->internals_random_at(0);
  Ymap->internals_random_at(0);
  Zmap->internals_random_at(0);
  Amap->internals_random_at(0);
  
  std::ofstream dot_file("complex-archi.dot");
  dot_file << archi->write_dot;  
  return 0;
}

implements this (the very complex graph can be viewed here.

The cxsom-builder architecture notation.
The cxsom-builder architecture notation.
Hervé Frezza-Buet,