Commit bbdcff15 authored by Michal Kravcenko's avatar Michal Kravcenko
Browse files

-added a support for multi-network systems sharing edge weights

-added an example testing multi-network error function
parent dfaf4073
/**
* DESCRIPTION OF THE FILE
*
* @author Michal Kravčenko
* @date 4.7.18 -
*/
#include "VertexConstant.h"
/**
* DESCRIPTION OF THE FILE
*
* @author Michal Kravčenko
* @date 4.7.18 -
*/
#ifndef INC_4NEURO_VERTEXCONSTANT_H
#define INC_4NEURO_VERTEXCONSTANT_H
class VertexConstant {
};
#endif //INC_4NEURO_VERTEXCONSTANT_H
/**
* DESCRIPTION OF THE FILE
*
* @author Michal Kravčenko
* @date 4.7.18 -
*/
#include "VertexSum.h"
/**
* DESCRIPTION OF THE FILE
*
* @author Michal Kravčenko
* @date 4.7.18 -
*/
#ifndef INC_4NEURO_VERTEXSUM_H
#define INC_4NEURO_VERTEXSUM_H
class VertexSum {
};
#endif //INC_4NEURO_VERTEXSUM_H
......@@ -7,15 +7,21 @@
#include "Connection.h"
Connection::Connection(Neuron *n_in, Neuron *n_out, ConnectionWeight* con) {
Connection* Connection::get_copy(Neuron *n_in, Neuron *n_out){
Connection *output = new Connection(n_in, n_out, this->con, false);
return output;
}
Connection::Connection(Neuron *n_in, Neuron *n_out, ConnectionWeight* con, bool del_weight) {
this->neuron_in = n_in;
this->neuron_out = n_out;
this->con = con;
this->delete_connection = del_weight;
}
Connection::~Connection() {
if(this->con){
if(this->con && this->delete_connection ){
delete this->con;
this->con = nullptr;
}
......
......@@ -34,18 +34,32 @@ private:
*/
Neuron *neuron_out = nullptr;
/**
*
*/
bool delete_connection = true;
//TODO pridat gradient pro ucely backpropagation
public:
/**
* Returns a new object reprresenting an edge using the same weight function as this one
* @param n_in
* @param n_out
* @return
*/
virtual Connection* get_copy(Neuron *n_in, Neuron *n_out) final;
/**
* Constructor
* @param[in] n_in Pointer to the Neuron on the receiving end of this connection
* @param[in] n_out Pointer to the Neuron on the signaling end of this connection
* @param[in] con Pointer to an object evaluating the weight function
*
* @param n_in
* @param n_out
* @param con
* @param delete_weight
*/
Connection(Neuron *n_in, Neuron *n_out, ConnectionWeight* con);
Connection(Neuron *n_in, Neuron *n_out, ConnectionWeight* con, bool delete_weight = true);
/**
* Destructor, deletes the 'con' object
......
......@@ -58,7 +58,7 @@ public:
/**
*
*/
~ConnectionWeight();
virtual ~ConnectionWeight();
/**
*
......
......@@ -15,8 +15,224 @@ NeuralNetwork::NeuralNetwork() {
this->connection_weights = new std::vector<double>(0);
this->connection_weights->reserve(0);
this->delete_weights = true;
}
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){
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 );
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_n_arr.push_back( new_neuron );
}
}
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]);
}
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 neuron is part of the subnetwork
Connection* new_connection = connection->get_copy( source_neuron, target_neuron );
source_neuron->add_connection_out(new_connection);
target_neuron->add_connection_in(new_connection);
}
}
}
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;
}
NeuralNetwork::~NeuralNetwork() {
if(this->neurons){
delete this->neurons;
......@@ -34,7 +250,8 @@ NeuralNetwork::~NeuralNetwork() {
delete this->active_eval_set;
this->active_eval_set = nullptr;
}
if(this->connection_weights){
if(this->connection_weights && this->delete_weights){
delete this->connection_weights;
this->connection_weights = nullptr;
}
......@@ -43,19 +260,20 @@ NeuralNetwork::~NeuralNetwork() {
int NeuralNetwork::add_neuron(Neuron *n) {
this->neurons->push_back(n);
this->in_out_determined = false;
n->set_idx(this->neurons->size() - 1);
return (int)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, int weight_idx) {
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, int weight_idx, double weight_value) {
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()){
......@@ -76,13 +294,13 @@ void NeuralNetwork::add_connection_simple(int n1_idx, int n2_idx, int weight_idx
}
void NeuralNetwork::add_connection_general(int n1_idx, int n2_idx, std::function<double(double *, int*, int)> *f,
int* weight_indices, double* weight_values, int n_weights) {
int* weight_indices, double* weight_values, size_t n_weights) {
ConnectionWeight *con_weight_u1u2 = new ConnectionWeight(n_weights, this->connection_weights, f);
ConnectionWeight *con_weight_u1u2 = new ConnectionWeight(n_weights, this->connection_weights);
//we analyze weights
int weight_idx = 0;
size_t weight_idx = 0;
double weight_value = 0.0;
for(int wi = 0; wi < n_weights; ++wi){
for(size_t wi = 0; wi < n_weights; ++wi){
weight_idx = weight_indices[wi];
weight_value = weight_values[wi];
......@@ -133,15 +351,25 @@ void NeuralNetwork::determine_inputs_outputs() {
}
}
this->n_inputs = (int)this->input_neurons->size();
this->n_outputs = (int)this->output_neurons->size();
this->n_inputs = this->input_neurons->size();
this->n_outputs = this->output_neurons->size();
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) {
if(!this->in_out_determined){
this->determine_inputs_outputs();
if(!this->in_out_determined && this->n_inputs * this->n_outputs <= 0){
// this->determine_inputs_outputs();
printf("Error: input and output neurons have not been specified\n");
return;
}
......@@ -234,10 +462,14 @@ void NeuralNetwork::copy_weights(double *weights) {
}
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)
//TODO catch exception when there are 0 neurons
double r = 4 * sqrt(6./(this->n_inputs + this->n_outputs));
boost::random::uniform_real_distribution<> dist(-r, r);
......@@ -260,4 +492,35 @@ size_t NeuralNetwork::get_n_weights() {
this->n_weights = this->connection_weights->size();
}
return this->n_weights;
}
\ No newline at end of file
}
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();
}
......@@ -64,6 +64,11 @@ private:
*/
bool in_out_determined = false;
/**
*
*/
bool delete_weights = true;
/**
*
*/
......@@ -74,6 +79,12 @@ private:
*/
void determine_inputs_outputs();
/**
*
* @param weight_ptr
*/
void set_weight_array( std::vector<double>* weight_ptr );
public:
/**
......@@ -81,11 +92,22 @@ public:
*/
NeuralNetwork();
/**
*
*/
~NeuralNetwork();
/**
* If possible, returns a neural net with 'input_neuron_indices' neurons as inputs and 'output_neuron_indices' as
* outputs, otherwise returns nullptr. The returned object shares adjustable weights with this network. All
* neurons are coppied (new instances), edges also. Uses a breadth-first search as the underlying algorithm.
* @param input_neuron_indices
* @param output_neuron_indices
* @return
*/
NeuralNetwork* get_subnet(std::vector<size_t> &input_neuron_indices, std::vector<size_t> &output_neuron_indices);
/**
*
* @param[in] input
......@@ -115,7 +137,7 @@ public:
* @param n2_idx
* @param weight_idx
*/
void add_connection_simple(int n1_idx, int n2_idx, int weight_idx);
void add_connection_simple(int n1_idx, int n2_idx, size_t weight_idx);
/**
*
......@@ -124,7 +146,7 @@ public:
* @param[in] weight_idx
* @param[in] weight_value
*/
void add_connection_simple(int n1_idx, int n2_idx, int weight_idx, double weight_value);
void add_connection_simple(int n1_idx, int n2_idx, size_t weight_idx, double weight_value);
/**
*
......@@ -136,7 +158,7 @@ public:
* @param n_weights
*/
void add_connection_general(int n1_idx, int n2_idx, std::function<double(double *, int*, int)> *f,
int* weight_indices, double* weight_values, int n_weights);
int* weight_indices, double* weight_values, size_t n_weights);
/**
*
......@@ -167,6 +189,18 @@ public:
*/
size_t get_n_weights();
/**
*
* @param input_neurons_indices
*/
void specify_input_neurons(std::vector<size_t> &input_neurons_indices);
/**
*
* @param output_neurons_indices
*/
void specify_output_neurons(std::vector<size_t> &output_neurons_indices);
};
......
......@@ -116,6 +116,14 @@ std::vector<Connection*>* Neuron::get_connections_out() {
return this->edges_out;
}
size_t Neuron::get_idx() {
return this->neural_net_index;
}
void Neuron::set_idx(size_t value) {
this->neural_net_index = value;
}
//template<class Archive>
//void Neuron::serialize(Archive & ar, const unsigned int version) {
// ar << this->potential;
......
......@@ -53,6 +53,11 @@ protected:
*/
unsigned int n_saturated_connections_out = 0;
/**
* Index of this neuron among the neurons in the neural network
*/
size_t neural_net_index;
/**
* A pointer to a vector containing pointers to incoming connections
*/
......@@ -76,6 +81,12 @@ protected:
public:
/**
* Instantiates a copy of this object and returns a pointer to it
* @return