-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathframework.hpp
More file actions
605 lines (454 loc) · 23.7 KB
/
framework.hpp
File metadata and controls
605 lines (454 loc) · 23.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
#pragma once
#ifndef FRAMEWORK_HPP
#define FRAMEWORK_HPP
#include <vector>
#include <map>
#include <memory>
#include <exception>
#include <stdexcept>
#include "robinhood/robin_map.h"
#include "nlohmann/json.hpp"
#include "utils/MOA.hpp"
/**
* The neuro namespace contains all of the classes defined by and used by the TENNLab
* open source neuromorphic computing framework. In general, the documentation for
* each component is in the markdown directory:
*
* Networks, Nodes and Edges: markdown/framework_network.md
* Processors: markdown/framework_processor.md
* Properties and PropertyPack: markdown/framework_properties.md
* Associated Data in Networks: markdown/framework_network_json_format.md
*/
namespace neuro
{
using std::map;
using std::string;
using std::vector;
using std::pair;
using std::tuple;
using std::unique_ptr;
using nlohmann::json;
class Property;
class PropertyPack;
class Node;
class Edge;
class Network;
class Processor;
struct Spike;
typedef pair<int,int> Coords;
/**
* This first group of definitions facilitates using a hash table for nodes and edges.
*/
class int_hash
{
public:
size_t operator() (const uint32_t val) const
{
// From MurmurHash3
size_t h = val;
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
};
/**
* A functor class for hashing pair values.
*/
class coord_hash
{
public:
/* See: https://stackoverflow.com/questions/5889238/why-is-xor-the-default-way-to-combine-hashes */
size_t operator() (const Coords &p) const
{
size_t l = int_hash{}(p.first);
size_t r = int_hash{}(p.second);
l ^= r + 0x9e3779b9 + (l << 6) + (l >> 2);
return l;
}
};
/* Use Hash Tables for storing a sparse collection of nodes / edges */
typedef tsl::robin_map<uint32_t, unique_ptr<Node>, int_hash> NodeMap;
typedef tsl::robin_map<Coords, unique_ptr<Edge>, coord_hash> EdgeMap;
/* TMP + Perfect Forwarding for C++11 // built into C++14 */
template<typename T, typename... Args>
unique_ptr<T> make_unique(Args&&... args)
{
return unique_ptr<T>(new T(std::forward<Args>(args)...));
}
/**
* The Property class represents a specific configurable property within a node, edge, or network.
* Please read the documentation in `markdown/framework_properties.md` for a description
* of both the `Property` and `Property_Pack` classes.
*/
class Property
{
public:
/** * Each property has an associated data type chosen from this enumeration. */
enum Type : signed char
{
INTEGER = 'I', /**< Numerical, integer value */
DOUBLE = 'D', /**< Numerical, double precision (64 bits) floating point value */
BOOLEAN = 'B', /**< Boolean value */
};
/* Four constructors, including copy and move constructors */
Property(const string& dname, int idx, int len, double dmin, double dmax, Type dtype);
Property(const json& j);
Property(const Property& p);
Property(Property &&p) noexcept;
/* Assignment overloads work. */
Property& operator= (const Property &p);
Property& operator= (Property &&p);
~Property() = default;
/* Json methods */
void to_json(json &j) const; /**< Add keys/vals to existing json */
json as_json() const; /**< Return json representation */
void from_json(const json &j); /**< Create from json */
string pretty_json() const; /**< Create a json string that's better than dump(). */
/* The data -- again, please read the markdown for descriptions. */
Type type; /**< Integer/Boolean/Double */
int index; /**< Starting index in the values vector */
int size; /**< Number of vector entries */
double min_value; /**< Minimum value that it can attain */
double max_value; /**< Maximum value that it can attain */
string name; /**< Name of the property */
};
/* Property equality in case you need it. */
bool operator==(const Property &lhs, const Property &rhs);
bool operator!=(const Property &lhs, const Property &rhs);
/**
* A PropertyPack encompasses a grouping of properties for a node, edge, and network
* all within the same structure. This ensures you have a coherent set of properties which
* make sense for your desired processor(s). Please see `markdown/framework_properties.md`
* for documentation.
*/
/* Properties are stored internally using a standard map. */
typedef std::map<string, Property> PropertyMap;
class PropertyPack
{
public:
void to_json(json &j) const; /**< Add keys/vals to existing json */
json as_json() const; /**< Return json representation */
void from_json(const json &j); /**< Create from json */
string pretty_json() const; /**< Create a json string that's better than dump(). */
void clear(); /**< Reset state to empty. */
PropertyMap nodes; /**< Map of properties for Node objects */
PropertyMap edges; /**< Map of properties for Edge objects */
PropertyMap networks; /**< Map of properties for Network objects */
size_t node_vec_size = 0; /**< How long the node data vector should be. */
size_t edge_vec_size = 0; /**< How long the edge data vector should be. */
size_t net_vec_size = 0; /**< How long the network data vector should be. */
/* Should allow the class to be "tie"-able */
/**
* This allows a PropertyPack to work like a native tuple. A developer can call
* `std::tie` to quickly dump the node, edge, and network property maps
* in a single line of code.
*/
operator tuple<PropertyMap&, PropertyMap&, PropertyMap&>()
{
return tuple<PropertyMap&, PropertyMap&, PropertyMap&>{nodes, edges, networks};
}
/* Adding properties to a property pack. */
int add_node_property(const string& name, double dmin, double dmax, Property::Type type, int cnt = 1);
int add_edge_property(const string& name, double dmin, double dmax, Property::Type type, int cnt = 1);
int add_network_property(const string& name, double dmin, double dmax, Property::Type type, int cnt = 1);
};
bool operator==(const PropertyPack &lhs, const PropertyPack &rhs);
bool operator!=(const PropertyPack &lhs, const PropertyPack &rhs);
/**
* The Node class represents a single node in a graph/network.
* Typically, for our use, this means it serves as a neuron.
* Please read the documentation in `markdown/framework_network.md for infomation
* about this class.
*/
class Node
{
public:
/* Constructors, assignment overleads, etc. */
Node(uint32_t idx, Network* n = nullptr) : id(idx), net(n) {};
Node(const Node &n) = delete;
Node(Node &&n) = delete;
Node& operator=(const Node &n) = delete;
Node& operator=(Node &&n) = delete;
~Node() = default;
/* Fields */
uint32_t id = 0; /**< Node ID */
int input_id = -1; /**< If it's an input node, the input id number. */
int output_id = -1; /**< If it's an output node, the output id number. */
const Network *net; /**< Pointer to the network that contains the node. */
vector<double> values; /**< Values defined by the PropertyPack */
vector<Edge*> incoming; /**< Incoming edges */
vector<Edge*> outgoing; /**< Outgoing edges */
vector <double> coordinates; /**< Optional -- useful if you are writing or using a visualization */
string name; /**< Optional -- can be useful for viz's or hand-tooling networks */
/* Getting / Setting values */
void set(int idx, double val); /**< Set a value by its index in values */
void set(const string& name, double val); /**< Set a value by its name in the PropertyPack */
double get(int idx); /**< Get a value by index. */
double get(const string& name); /**< Get a value by name. */
/* Hidden / Input / Output */
inline bool is_hidden() const /**< Is the node hidden? */
{ return !is_input() && !is_output(); }
inline bool is_input() const /**< Is the node an input node? */
{ return input_id >= 0; }
inline bool is_output() const /**< Is the node an output node? */
{ return output_id >= 0; }
};
/**
* The Edge class serves as a directed edge between two nodes in a network.
* Typically, this means it will serve as a synapse between two nodes.
* Please read the documentation in `markdown/framework_network.md for infomation
* about this class.
*/
class Edge
{
public:
/* Constructors, assignment overleads, etc. */
Edge(Node *f, Node *t, Network *n = nullptr) : from(f), to(t), net(n) {}
Edge(const Edge &e) = delete;
Edge(Edge &&e) = delete;
Edge& operator=(const Edge &n) = delete;
Edge& operator=(Edge &&n) = delete;
~Edge() = default;
/* Fields */
Node* from; /**< The node that the edge is coming from. */
Node* to; /**< The node that the edge is going to. */
const Network *net; /**< Pointer to the network that contains the node. */
vector<double> values; /**< Values defined by the PropertyPack */
json as_json() const; /**< Turn it into a json object */
vector <double> control_point; /**< Optional. Bezier control point(s) for displaying. */
/* Getting / Setting values */
void set(int idx, double val); /**< Set a value by its index in values */
void set(const string& name, double val); /**< Set a value by its name in the PropertyPack */
double get(int idx); /**< Get a value by index. */
double get(const string& name); /**< Get a value by name. */
};
/**
* The Network class contains a directed graph of nodes and edges along with the
* associated properties to describe the characteristics of each component.
* Please read the documentation in `markdown/framework_network.md for infomation
* about this class.
*/
class Network
{
public:
/* Constructors, assignment overloads, etc. */
Network() = default;
Network(const Network &net);
Network(Network &&net);
Network& operator=(const Network &net);
Network& operator=(Network &&net);
~Network() noexcept;
bool operator==(const Network &rhs) const;
void clear(bool include_properties); /**< Clear network, optionally clear properties. */
/* JSON methods */
void to_json(json &j) const; /**< Add keys/vals to existing json */
json as_json() const; /**< Return json representation */
void from_json(const json &j); /**< Create from json */
string pretty_json() const; /**< Create a json string that's better than dump(). */
string pretty_nodes() const; /**< Create a nice json string of the nodes. */
string pretty_edges() const; /**< Create a nice json string of the edges. */
/* Properties / PropertyPack */
void set_properties(const PropertyPack& properties); /**< Set the PropertyPack */
PropertyPack get_properties() const; /**< Get the PropertyPack */
bool is_node_property(const string& name) const; /**< Query the node properties for name. */
bool is_edge_property(const string& name) const; /**< Query the edge properties for name. */
bool is_network_property(const string& name) const; /**< Query the network properties for name. */
const Property* get_node_property(const string& name) const; /**< Get the node property. */
const Property* get_edge_property(const string& name) const; /**< Get the edge property. */
const Property* get_network_property(const string& name) const; /**< Get the network property. */
/* Adding / modifying / deleting nodes and edges */
Node* add_node(uint32_t idx); /**< Create node with id */
bool is_node(uint32_t idx) const; /**< Does node with id exist? */
Node* get_node(uint32_t idx) const; /**< Return node with id */
Node* add_or_get_node(uint32_t idx); /**< Return node with id , or create it */
void remove_node(uint32_t idx, bool force = false); /**< Delete node - if force=false, error on IO nodes. */
void rename_node(uint32_t old_name, uint32_t new_name); /**< Change node's id */
Edge* add_edge(uint32_t fr, uint32_t to); /**< Analogous to add_node() */
bool is_edge(uint32_t fr, uint32_t to) const; /**< Analogous to is_node() */
Edge* get_edge(uint32_t fr, uint32_t to) const; /**< Analogous to get_node() */
Edge* add_or_get_edge(uint32_t fr, uint32_t to); /**< Analogous to add_or_get_node() */
void remove_edge(uint32_t fr, uint32_t to); /**< Analogous to remove_node() */
/* Input and output nodes */
int add_input(uint32_t idx); /**< Add the next input */
Node* get_input(int input_id) const; /**< Return a pointer to the given input */
int num_inputs() const; /**< The number of input nodes */
int add_output(uint32_t idx); /**< Add the next output */
Node* get_output(int output_id) const; /**< Return a pointer to the given output */
int num_outputs() const; /**< The number of output nodes */
/* The Associated Data JSON */
void set_data(const string& name, const json& data); /**< Add/Set key/val pair to JSON */
json get_data(const string& name) const; /**< Get json for the given key */
vector<string> data_keys() const; /**< Get all of the keys */
/* Methods to help do randomization */
Node* get_random_node(MOA &moa) const; /**< Get a random node */
Edge* get_random_edge(MOA &moa) const; /**< Get a random edge */
Node* get_random_input(MOA &moa) const; /**< Get a random input node */
Node* get_random_output(MOA &moa) const; /**< Get a random output node */
void randomize_properties(MOA &moa); /**< Randomize network values */
void randomize_properties(MOA &moa, Node *n); /**< Randomize node values */
void randomize_properties(MOA &moa, Edge *e); /**< Randomize edge values */
void randomize_property(MOA& moa, const string& pname); /**< Randomize single value */
void randomize_property(MOA& moa, Node *n, const string& pname); /**< Randomize single value */
void randomize_property(MOA& moa, Edge *n, const string& pname); /**< Randomize single value */
void randomize_property(MOA& moa, /**< Randomize a single value in the vector. */
const Property& p,
vector<double>& values);
void randomize(const json& params); /**< Generate a random network (unimplemented) */
/* Pruning and Sorting */
void prune(); /**< Prune nodes and edges not on an I/O path */
void make_sorted_node_vector(); /**< Sort the nodes by id, nothing if already sorted. */
vector <Node *> sorted_node_vector; /**< The sorted nodes */
/* Iterators and Metadata */
NodeMap::iterator begin(); /**< Beginning of the nodes in the node hash table. */
NodeMap::iterator end(); /**< End of the nodes in the node hash table. */
EdgeMap::iterator edges_begin(); /**< Beginning of the edges in the edge hash table. */
EdgeMap::iterator edges_end(); /**< End of the edges in the edge hash table. */
size_t num_nodes() const; /**< Number of nodes. */
size_t num_edges() const; /**< Number of edges. */
/* The values defined by the PropertyPack */
vector<double> values;
protected:
/* helpers for move/copy operations */
void copy_from(const Network& net);
void move_from(Network&& net);
/* return a random value appropriate for the given property */
double random_value(MOA &moa, const Property &p);
/* random network -- edges with probability p */
void randomize_p(const json& params);
/* random network -- input -> hidden -> output */
void randomize_h(const json& params);
/* inputs/outputs -- index = input/output id, value = node id */
vector<uint32_t> m_inputs;
vector<uint32_t> m_outputs;
/* Nodes and Edges for the network stored centrally */
NodeMap m_nodes;
EdgeMap m_edges;
PropertyPack m_properties;
/* dictionary of associated data (e.g. encoder params, app params, etc.) */
json m_associated_data = {};
friend class Node;
friend class Edge;
};
/**
* A Spike is a simple data structure to represent a tuple of id, time, and value.
*/
struct Spike
{
int id; /**< Represents the input id of the destination neuron. */
double time; /**< Represents the timing of when the spike should arrive. */
double value; /**< On platforms which support variable values, this is the charge to accumulate. */
Spike(int id_, double time_, double value_) :
id(id_), time(time_), value(value_) {}
};
/**
* The Processor class is an interface for neuromorphic simulators and hardware.
* This interface specifies the necessary methods to interact seamlessly with
* the rest of the framework. In other words, when you want to implement a
* simulator for a processor (or implement the systems code to connect a physical
* processor to the framework), then these are the methods that you need to
* implement. Please read the documentation in markdown/framework_processor (relative
* to the main framework directory) for information about these methods and implementing
* a processor to match this interface.
*/
class Processor
{
public:
virtual ~Processor() {}
static Processor *make(const string &name, json ¶ms);
/* Load or clear a network. */
virtual bool load_network(Network* n, int network_id = 0) = 0;
virtual bool load_networks(std::vector<Network*> &n) = 0;
virtual void clear(int network_id = 0) = 0;
/* Queue spike(s) as input to a network or to multiple networks.
When normalized == true (the default), spike values should be between
-1 and 1, and then the processor can convert its value to the proper range.
When normalized == false, the value is simply passed to the neuroprocessor. */
virtual void apply_spike(const Spike& s, bool normalized = true, int network_id = 0) = 0;
virtual void apply_spike(const Spike& s,
const vector<int>& network_ids,
bool normalized = true) = 0;
virtual void apply_spikes(const vector<Spike>& s,
bool normalized = true,
int network_id = 0) = 0;
virtual void apply_spikes(const vector<Spike>& s,
const vector<int>& network_ids,
bool normalized = true) = 0;
/* Run the network(s) for the desired time with queued input(s) */
virtual void run(double duration, int network_id = 0) = 0;
virtual void run(double duration, const vector<int>& network_ids) = 0;
/* Get processor time based on specified network */
virtual double get_time(int network_id = 0) = 0;
/* Output tracking. See the markdown for a detailed description of these. */
virtual bool track_output_events(int output_id, bool track = true, int network_id = 0) = 0;
virtual bool track_neuron_events(uint32_t node_id, bool track = true, int network_id = 0) = 0;
/* Access output spike data */
virtual double output_last_fire(int output_id, int network_id = 0) = 0;
virtual vector <double> output_last_fires(int network_id = 0) = 0;
virtual int output_count(int output_id, int network_id = 0) = 0;
virtual vector <int> output_counts(int network_id = 0) = 0;
virtual vector <double> output_vector(int output_id, int network_id = 0) = 0;
virtual vector < vector <double> > output_vectors(int network_id = 0) = 0;
/* Spike data from all neurons. */
virtual long long total_neuron_counts(int network_id = 0) = 0;
virtual long long total_neuron_accumulates(int network_id = 0) = 0;
virtual vector <int> neuron_counts(int network_id = 0) = 0;
virtual vector <double> neuron_last_fires(int network_id = 0) = 0;
virtual vector < vector <double> > neuron_vectors(int network_id = 0) = 0;
/* Charge data from all neurons. */
virtual vector <double> neuron_charges(int network_id = 0) = 0;
/* Synapse data from all synapses. */
virtual void synapse_weights (vector <uint32_t> &pres,
vector <uint32_t> &posts,
vector <double> &vals,
int network_id = 0) = 0;
/* Remove state, keep network loaded */
virtual void clear_activity(int network_id = 0) = 0;
/* Network and Processor Properties. The network properties correspond to the Data
field in the network, nodes and edges. The processor properties are so that
applications may query the processor for various properties (e.g. input scaling,
fire-on-threshold vs fire-over-threshold). */
virtual PropertyPack get_network_properties() const = 0;
virtual json get_processor_properties() const = 0;
/* get_params() returns the json that you can use to recreate the processor.
It doesn't have to be the same json as was used to create the processor; just
json to create the same processor. */
virtual json get_params() const = 0;
/* get_name() returns the name of the processor. */
virtual string get_name() const = 0;
};
/**
* Helper procedures for applications using processors.
*/
/* This calls track_output_events for all of the output neurons in the given network.
If it fails, it will undo the track_output_events() calls that it made previously. */
bool track_all_output_events(Processor *p, Network *n, int network_id = 0);
bool track_all_neuron_events(Processor *p, Network *n, int network_id = 0);
/* If node n is the ith smallest node by node id, and the jth smallest node
in the ordering of the NodeMap then rv[i].second = j, and rv[i].first = the
node's id. This helps deal with the return values to neuron_counts(),
neuron_last_fires(), neuron_vectors() and neuron_charges(), and also with the fact that
nodes are stored in a hash table. */
json neuron_last_fires_to_json(const vector <double> &last_fires, Network *n);
json neuron_counts_to_json(const vector <int> &counts, Network *n);
json neuron_charges_to_json(const vector <double> &charges, Network *n);
json neuron_vectors_to_json(const vector < vector <double> > &events,
const string &type,
Network *n);
/* This is a heavyweight procedure. It assumes that *n* has been loaded on *p*. It
will call run(1) duration times, and then return a json with two keys. Each val
is a vector of vectors. The outer vector has an entry for each timestep.
"spike_raster": The inner vector is 0 or 1 for each neuron. The values are presented in
the same order as sorted_node_vector.
"charges": The inner vector contains the charge of each neuron (doubles). The values
are presented in the same order as sorted_node_vector.
*/
json run_and_track(int duration, Processor *p, int network_id = 0);
/* This copies n, but sets the synapse weights from the network that is on the processor. */
Network *pull_network(Processor *p, Network *n, int network_id = 0);
/* Use the spike raster to emit the appropriate apply_spikes() calls. */
void apply_spike_raster(Processor *p, int in_neuron, const vector <char> &sr, int network_id = 0);
} // End of neuro namespace.
#endif