Newer
Older

Michal Kravcenko
committed
/**
* DESCRIPTION OF THE FILE
*
* @author Michal Kravčenko
* @date 13.6.18 -
*/
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real_distribution.hpp>

Michal Kravcenko
committed
#include "NeuralNetwork.h"
#include "../NetConnection/ConnectionWeightIdentity.h"

Michal Kravcenko
committed
NeuralNetwork::NeuralNetwork() {
this->neurons = new std::vector<Neuron*>(0);
this->connection_weights = new std::vector<double>(0);
this->connection_weights->reserve(0);
this->delete_weights = true;

Michal Kravcenko
committed
}
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
NeuralNetwork* NeuralNetwork::get_subnet(std::vector<size_t> &input_neuron_indices, std::vector<size_t> &output_neuron_indices){
NeuralNetwork *output_net = nullptr;
Neuron * active_neuron, * target_neuron;
size_t n = this->neurons->size();
bool *part_of_subnetwork = new bool[n];
std::fill(part_of_subnetwork, part_of_subnetwork + n, false);
bool *is_reachable_from_source = new bool[n];
bool *is_reachable_from_destination = new bool[n];
std::fill(is_reachable_from_source, is_reachable_from_source + n, false);
std::fill(is_reachable_from_destination, is_reachable_from_destination + n, false);
bool *visited_neurons = new bool[n];
std::fill(visited_neurons, visited_neurons + n, false);
size_t active_set_size[2];
active_set_size[0] = 0;
active_set_size[1] = 0;
size_t * active_neuron_set = new size_t[2 * n];
size_t idx1 = 0, idx2 = 1;
/* MAPPING BETWEEN NEURONS AND THEIR INDICES */
size_t idx = 0, idx_target;
for(Neuron *v: *this->neurons){
v->set_idx( idx );
idx++;
}
/* INITIAL STATE FOR THE FORWARD PASS */
for(size_t i: input_neuron_indices ){
if( i < 0 || i >= n){
//invalid index
continue;
}
active_neuron_set[idx1 * n + active_set_size[idx1]] = i;
active_set_size[idx1]++;
visited_neurons[i] = true;
}
/* FORWARD PASS */
while(active_set_size[idx1] > 0){
//we iterate through the active neurons and propagate the signal
for(int i = 0; i < active_set_size[idx1]; ++i){
idx = active_neuron_set[i];
is_reachable_from_source[ idx ] = true;
active_neuron = this->neurons->at( idx );
for(Connection* connection: *(active_neuron->get_connections_out())){
target_neuron = connection->get_neuron_out( );
idx_target = target_neuron->get_idx( );
if( visited_neurons[idx_target] ){
//this neuron was already analyzed
continue;
}
visited_neurons[idx_target] = true;
active_neuron_set[active_set_size[idx2] + n * idx2] = idx_target;
active_set_size[idx2]++;
}
}
idx1 = idx2;
idx2 = (idx1 + 1) % 2;
active_set_size[idx2] = 0;
}
/* INITIAL STATE FOR THE FORWARD PASS */
active_set_size[0] = active_set_size[1] = 0;
std::fill(visited_neurons, visited_neurons + n, false);
for(size_t i: output_neuron_indices ){
if( i < 0 || i >= n){
//invalid index
continue;
}
active_neuron_set[idx1 * n + active_set_size[idx1]] = i;
active_set_size[idx1]++;
visited_neurons[i] = true;
}
/* BACKWARD PASS */
size_t n_new_neurons = 0;
while(active_set_size[idx1] > 0){
//we iterate through the active neurons and propagate the signal
for(int i = 0; i < active_set_size[idx1]; ++i){
idx = active_neuron_set[i];
is_reachable_from_destination[ idx ] = true;
active_neuron = this->neurons->at( idx );
if(is_reachable_from_source[ idx ]){
n_new_neurons++;
}
for(Connection* connection: *(active_neuron->get_connections_in())){
target_neuron = connection->get_neuron_in( );
idx_target = target_neuron->get_idx( );
if( visited_neurons[idx_target] ){
//this neuron was already analyzed
continue;
}
visited_neurons[idx_target] = true;
active_neuron_set[active_set_size[idx2] + n * idx2] = idx_target;
active_set_size[idx2]++;
}
}
idx1 = idx2;
idx2 = (idx1 + 1) % 2;
active_set_size[idx2] = 0;
}
/* FOR CONSISTENCY REASONS */
for(size_t in: input_neuron_indices){
if( !is_reachable_from_destination[in] ){
n_new_neurons++;
}
is_reachable_from_destination[in] = true;
}
/* FOR CONSISTENCY REASONS */
for(size_t in: output_neuron_indices){
if( !is_reachable_from_source[in] ){
n_new_neurons++;
}
is_reachable_from_source[in] = true;
}
/* WE FORM THE SET OF NEURONS IN THE OUTPUT NETWORK */
if(n_new_neurons > 0){
// printf("Number of new neurons: %d\n", n_new_neurons);
output_net = new NeuralNetwork();
output_net->set_weight_array( this->connection_weights );
std::vector<size_t > local_inputs(0), local_outputs(0);
local_inputs.reserve(input_neuron_indices.size());
local_outputs.reserve(output_neuron_indices.size());
std::vector<Neuron*> local_n_arr(0);
local_n_arr.reserve( n_new_neurons );
std::vector<Neuron*> local_local_n_arr(0);
local_local_n_arr.reserve( n_new_neurons );
int * neuron_local_mapping = new int[ n ];
std::fill(neuron_local_mapping, neuron_local_mapping + n, -1);
idx = 0;
for(size_t i = 0; i < n; ++i){
if(is_reachable_from_source[i] && is_reachable_from_destination[i]){
neuron_local_mapping[i] = (int)idx;
idx++;
Neuron *new_neuron = this->neurons->at(i)->get_copy( );
output_net->add_neuron( new_neuron );
local_local_n_arr.push_back( new_neuron );
local_n_arr.push_back( this->neurons->at(i) );
}
}
for(size_t in: input_neuron_indices){
local_inputs.push_back(neuron_local_mapping[in]);
}
for(size_t in: output_neuron_indices){
local_outputs.push_back(neuron_local_mapping[in]);
}
// printf("%d\n", local_n_arr.size());
// printf("inputs: %d, outputs: %d\n", local_inputs.size(), local_outputs.size());
int local_idx_1, local_idx_2;
for(Neuron* source_neuron: local_n_arr){
//we also add the relevant edges
local_idx_1 = neuron_local_mapping[source_neuron->get_idx()];
for(Connection* connection: *(source_neuron->get_connections_out( ))){
target_neuron = connection->get_neuron_out();
local_idx_2 = neuron_local_mapping[target_neuron->get_idx()];
if(local_idx_2 >= 0){
//this edge is part of the subnetwork
Connection* new_connection = connection->get_copy( local_local_n_arr[local_idx_1], local_local_n_arr[local_idx_2] );
local_local_n_arr[local_idx_1]->add_connection_out(new_connection);
local_local_n_arr[local_idx_2]->add_connection_in(new_connection);
// printf("adding a connection between neurons %d, %d\n", local_idx_1, local_idx_2);
}
}
}
output_net->specify_input_neurons(local_inputs);
output_net->specify_output_neurons(local_outputs);
delete [] neuron_local_mapping;
}
delete [] is_reachable_from_source;
delete [] is_reachable_from_destination;
delete [] part_of_subnetwork;
delete [] visited_neurons;
delete [] active_neuron_set;
return output_net;
}

Michal Kravcenko
committed
NeuralNetwork::~NeuralNetwork() {
if(this->neurons){
delete this->neurons;
this->neurons = nullptr;
}
if(this->output_neurons){
delete this->output_neurons;
this->output_neurons = nullptr;
}
if(this->input_neurons){
delete this->input_neurons;
this->input_neurons = nullptr;
}
if(this->active_eval_set){
delete this->active_eval_set;
this->active_eval_set = nullptr;
}
if(this->connection_weights && this->delete_weights){
delete this->connection_weights;
this->connection_weights = nullptr;
}

Michal Kravcenko
committed
}
int NeuralNetwork::add_neuron(Neuron *n) {

Michal Kravcenko
committed
this->neurons->push_back(n);
this->in_out_determined = false;
n->set_idx(this->neurons->size() - 1);
return n->get_idx();
}
void NeuralNetwork::add_connection_simple(int n1_idx, int n2_idx) {
add_connection_simple(n1_idx, n2_idx, -1);
}
void NeuralNetwork::add_connection_simple(int n1_idx, int n2_idx, size_t weight_idx) {
add_connection_simple(n1_idx, n2_idx, weight_idx, 1);
void NeuralNetwork::add_connection_simple(int n1_idx, int n2_idx, size_t weight_idx, double weight_value) {
// TODO generate weight_value automatically from normal distribution
if(weight_idx < 0 || weight_idx >= this->connection_weights->size()){
//this weight is a new one, we add it to the system of weights
this->connection_weights->push_back(weight_value);
weight_idx = (int)this->connection_weights->size() - 1;
}
Neuron *neuron_out = this->neurons->at(n1_idx);
Neuron *neuron_in = this->neurons->at(n2_idx);
ConnectionWeightIdentity *con_weight_u1u2 = new ConnectionWeightIdentity(this->connection_weights);
con_weight_u1u2->SetParamIndex(weight_idx, 0);
Connection *u1u2 = new Connection(neuron_out, neuron_in, con_weight_u1u2);
neuron_out->add_connection_out(u1u2);
neuron_in->add_connection_in(u1u2);
}
void NeuralNetwork::add_connection_general(int n1_idx, int n2_idx, std::function<double(double *, int*, int)> *f,
int* weight_indices, double* weight_values, size_t n_weights) {
ConnectionWeight *con_weight_u1u2 = new ConnectionWeight(n_weights, this->connection_weights);
//we analyze weights
size_t weight_idx = 0;
double weight_value = 0.0;
for(size_t wi = 0; wi < n_weights; ++wi){
weight_idx = weight_indices[wi];
weight_value = weight_values[wi];
if(weight_idx < 0 || weight_idx >= this->connection_weights->size()){
//this weight is a new one, we add it to the system of weights
this->connection_weights->push_back(weight_value);
weight_indices[wi] = (int)this->connection_weights->size() - 1;
}
con_weight_u1u2->SetParamIndex(weight_indices[wi], wi);
}
Neuron *neuron_out = this->neurons->at(n1_idx);
Neuron *neuron_in = this->neurons->at(n2_idx);
Connection *u1u2 = new Connection(neuron_out, neuron_in, con_weight_u1u2);
neuron_out->add_connection_out(u1u2);
neuron_in->add_connection_in(u1u2);

Michal Kravcenko
committed
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
}
void NeuralNetwork::determine_inputs_outputs() {
if(this->output_neurons){
delete this->output_neurons;
}
if(this->input_neurons){
delete this->input_neurons;
}
this->output_neurons = new std::vector<Neuron*>(0);
this->input_neurons = new std::vector<Neuron*>(0);
if(this->active_eval_set == nullptr){
this->active_eval_set = new std::vector<Neuron*>(this->neurons->size() * 2);
}
else{
this->active_eval_set->resize(this->neurons->size() * 2);
}
for(Neuron* neuron: *this->neurons){
if(neuron->get_connections_out()->empty()){
//this neuron has no outgoing connections, it is the output neuron
this->output_neurons->push_back(neuron);
}
else if(neuron->get_connections_in()->empty()){
//this neuron has no incoming connections, it is the input neuron
this->input_neurons->push_back(neuron);
}
}
this->n_inputs = this->input_neurons->size();
this->n_outputs = this->output_neurons->size();

Michal Kravcenko
committed
this->in_out_determined = true;
}
void NeuralNetwork::set_weight_array(std::vector<double> *weight_ptr) {
if(this->connection_weights){
delete this->connection_weights;
}
this->connection_weights = weight_ptr;
this->delete_weights = false;
}
void NeuralNetwork::eval_single(std::vector<double> &input, std::vector<double> &output, double * custom_weights) {
if(!this->in_out_determined && this->n_inputs * this->n_outputs <= 0){
// this->determine_inputs_outputs();
std::cerr << "Input and output neurons have not been specified\n" << std::endl;

Michal Kravcenko
committed
}
if(this->n_inputs != input.size()){
std::cerr << "Error, input size != Network input size\n" << std::endl;
exit(-1);

Michal Kravcenko
committed
}
if(this->n_outputs != output.size()){
std::cerr << "Error, output size != Network output size\n" << std::endl;
exit(-1);

Michal Kravcenko
committed
}
// std::vector<double> *weight_ptr = this->connection_weights;
// std::vector<double> *custom_weight_ptr = nullptr;
//
//
if(custom_weights != nullptr){
// custom_weight_ptr = new std::vector<double>(custom_weights, custom_weights + this->n_weights);
// this->connection_weights = custom_weight_ptr;
this->connection_weights->assign(custom_weights, custom_weights + this->n_weights);
}

Michal Kravcenko
committed
std::fill(output.begin(), output.end(), 0.0);
//reset of the potentials
for(Neuron* neuron: *this->neurons){
neuron->set_potential(0.0);
neuron->set_saturation_in(0);
neuron->set_saturation_out(0);
}
if(this->active_eval_set == nullptr){
this->active_eval_set = new std::vector<Neuron*>(this->neurons->size() * 2);
}
else{
this->active_eval_set->resize(this->neurons->size() * 2);
}

Michal Kravcenko
committed
int active_set_size[2];
active_set_size[0] = 0;
active_set_size[1] = 0;
int idx1 = 0, idx2 = 1;
active_set_size[0] = this->n_inputs;
int i = 0;
auto n = this->neurons->size();
for(Neuron* neuron: *this->input_neurons){
this->active_eval_set->at(i) = neuron;
neuron->set_potential(input[i]);
// printf("INPUT NEURON %d, POTENTIAL: %f\n", neuron, input[i]);

Michal Kravcenko
committed
++i;
}
Neuron* active_neuron;
Neuron* target_neuron;
while(active_set_size[idx1] > 0){
//we iterate through the active neurons and propagate the signal
for(i = 0; i < active_set_size[idx1]; ++i){
active_neuron = this->active_eval_set->at(i + n * idx1);
active_neuron->activate();
// printf(" active neuron %d, state: %f\n", active_neuron, active_neuron->get_state());

Michal Kravcenko
committed
for(Connection* connection: *(active_neuron->get_connections_out())){
connection->pass_signal();
target_neuron = connection->get_neuron_out();
target_neuron->adjust_saturation_in(1);
if(target_neuron->is_saturated_in()){
this->active_eval_set->at(active_set_size[idx2] + n * idx2) = target_neuron;
active_set_size[idx2]++;
}
}
}
idx1 = idx2;
idx2 = (idx1 + 1) % 2;
active_set_size[idx2] = 0;
}
i = 0;
for(Neuron* neuron: *this->output_neurons){
output[i] = neuron->get_state();

Michal Kravcenko
committed
// printf("OUTPUT NEURON %d, VALUE: %f\n", neuron, output[i]);

Michal Kravcenko
committed

Michal Kravcenko
committed
++i;
}

Michal Kravcenko
committed

Michal Kravcenko
committed
}
void NeuralNetwork::copy_weights(double *weights) {
for(unsigned int i = 0; i < this->connection_weights->size(); ++i){
(*this->connection_weights)[i] = weights[i];
}
}
void NeuralNetwork::randomize_weights() {
if( this->neurons->size() <= 0 || !this->in_out_determined ){
return;
}
boost::random::mt19937 gen;
// Init weight guess ("optimal" for logistic activation functions)
double r = 4 * sqrt(6./(this->n_inputs + this->n_outputs));
boost::random::uniform_real_distribution<> dist(-r, r);
for(unsigned int i = 0; i < this->n_weights; i++) {
this->connection_weights->at(i) = dist(gen);
}
}
size_t NeuralNetwork::get_n_inputs() {
return this->n_inputs;
}
size_t NeuralNetwork::get_n_outputs() {
return this->n_outputs;
}
size_t NeuralNetwork::get_n_weights() {
if(!this->n_weights) {
this->n_weights = this->connection_weights->size();
}
return this->n_weights;
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
}
void NeuralNetwork::specify_input_neurons(std::vector<size_t> &input_neurons_indices) {
if( !this->input_neurons ){
this->input_neurons = new std::vector<Neuron*>();
}
this->input_neurons->reserve( input_neurons_indices.size() );
for( size_t e: input_neurons_indices ){
if( e < 0 || e >= this->neurons->size() ){
continue;
}
this->input_neurons->push_back( this->neurons->at(e) );
}
this->n_inputs = this->input_neurons->size();
}
void NeuralNetwork::specify_output_neurons(std::vector<size_t> &output_neurons_indices) {
if( !this->output_neurons ){
this->output_neurons = new std::vector<Neuron*>();
}
this->output_neurons->reserve( output_neurons_indices.size() );
for( size_t e: output_neurons_indices ){
if( e < 0 || e >= this->neurons->size() ){
continue;
}
this->output_neurons->push_back( this->neurons->at(e) );
}
this->n_outputs = this->output_neurons->size();
}