From a3c47005ae08fa8eda2523bb2f9fc47c250a13f9 Mon Sep 17 00:00:00 2001
From: Michal Kravcenko <michal.kravcenko@vsb.cz>
Date: Fri, 15 Jun 2018 12:24:08 +0200
Subject: [PATCH] - cleaned up the way neural nets are initialized and added
 some docs

---
 .idea/usage.statistics.xml                   |  4 +-
 src/NetConnection/Connection.cpp             |  9 ++--
 src/NetConnection/Connection.h               | 52 +++++++++---------
 src/NetConnection/ConnectionWeight.cpp       |  4 +-
 src/NetConnection/ConnectionWeight.h         |  4 +-
 src/NetConnection/ConnectionWeightIdentity.h |  7 +++
 src/Network/NeuralNetwork.cpp                | 56 +++++++++++++++++++-
 src/Network/NeuralNetwork.h                  | 27 +++++++++-
 src/Neuron/Neuron.cpp                        |  3 ++
 src/main.cpp                                 | 52 +++++++++---------
 10 files changed, 152 insertions(+), 66 deletions(-)

diff --git a/.idea/usage.statistics.xml b/.idea/usage.statistics.xml
index 317c2d7f..f6e9b451 100644
--- a/.idea/usage.statistics.xml
+++ b/.idea/usage.statistics.xml
@@ -5,7 +5,7 @@
       <usages-collector id="statistics.file.extensions.open">
         <counts>
           <entry key="Makefile" value="1" />
-          <entry key="cpp" value="58" />
+          <entry key="cpp" value="59" />
           <entry key="f90" value="4" />
           <entry key="h" value="71" />
           <entry key="txt" value="7" />
@@ -14,7 +14,7 @@
       <usages-collector id="statistics.file.types.open">
         <counts>
           <entry key="CMakeLists.txt" value="7" />
-          <entry key="ObjectiveC" value="129" />
+          <entry key="ObjectiveC" value="130" />
           <entry key="PLAIN_TEXT" value="5" />
         </counts>
       </usages-collector>
diff --git a/src/NetConnection/Connection.cpp b/src/NetConnection/Connection.cpp
index a11c1703..d36db016 100644
--- a/src/NetConnection/Connection.cpp
+++ b/src/NetConnection/Connection.cpp
@@ -14,9 +14,12 @@ Connection::Connection(Neuron *n_in, Neuron *n_out, ConnectionWeight* con) {
     this->con = con;
 }
 
-//Connection::~Connection() {
- //
-//}
+Connection::~Connection() {
+    if(this->con){
+        delete this->con;
+        this->con = nullptr;
+    }
+}
 
 void Connection::adjust_weights(double* values) {
     this->con->adjust_weights(values);
diff --git a/src/NetConnection/Connection.h b/src/NetConnection/Connection.h
index bdcbfb3c..f6c166c9 100644
--- a/src/NetConnection/Connection.h
+++ b/src/NetConnection/Connection.h
@@ -15,77 +15,73 @@ class Neuron;
 class ConnectionWeight;
 
 /**
- *
+ * Class representing directed connection between two neurons
  */
 class Connection {
 private:
 
     /**
-     *
+     * Pointer to an object evaluating the weight function
      */
     ConnectionWeight *con = nullptr;
     /**
-     *
+     * Pointer to the Neuron on the receiving end of this connection
      */
     Neuron *neuron_in = nullptr;
 
     /**
-     *
+     * Pointer to the Neuron on the signaling end of this connection
      */
     Neuron *neuron_out = nullptr;
 
 
-    //TODO pridat gradient
+    //TODO pridat gradient pro ucely backpropagation
 
 public:
 
-    /**
-     *
-     * @param[in] n_in
-     * @param[in] n_out
-     */
+   /**
+    * 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
+    */
     Connection(Neuron *n_in, Neuron *n_out, ConnectionWeight* con);
 
-//    Connection(Neuron *n_in, Neuron *n_out, Connection* ref_con);
-
     /**
-     *
+     * Destructor, deletes the 'con' object
      */
-    ~Connection()=default;
+    ~Connection();
 
     /**
-     *
-     * @param[in] values
+     * Takes the array of double values and alters the corresponding weights associated
+     * with the 'con' object
+     * @param[in] values Values to be added to the associated weights
      */
     void adjust_weights(double *values);
 
     /**
-     *
-     * @param[in] values
+     * Takes the array of double values and sets the corresponding weights associated
+     * with the 'con' object
+     * @param[in] values Values to be set to the associated weights
      */
     void set_weights(double *values);
 
-//    /**
-//     *
-//     * @return
-//     */
-//    double get_weight();
-
-
     /**
-     *
+     * Takes the output signal of Neuron 'neuron_out', multiplies it by the result of the
+     * weight function associated with this connection and adds the result to the potential
+     * of the Neuron 'neuron_in'
      */
     void pass_signal();
 
 
     /**
-     *
+     * Returns the pointer to the Neuron on the receiving end of this connection
      * @return
      */
     Neuron* get_neuron_in();
 
     /**
-     *
+     * Returns the pointer to the Neuron on the signaling end of this connection
      * @return
      */
     Neuron* get_neuron_out();
diff --git a/src/NetConnection/ConnectionWeight.cpp b/src/NetConnection/ConnectionWeight.cpp
index b4fdf7e2..cc1efb34 100644
--- a/src/NetConnection/ConnectionWeight.cpp
+++ b/src/NetConnection/ConnectionWeight.cpp
@@ -12,7 +12,7 @@ ConnectionWeight::ConnectionWeight() {
 
 }
 
-ConnectionWeight::ConnectionWeight(int param_count, std::function<double(double **, int)> f) {
+ConnectionWeight::ConnectionWeight(int param_count, std::function<double(double **, int)> *f) {
     this->param_ptrs = new double*[param_count];
     this->n_params = param_count;
 
@@ -49,5 +49,5 @@ void ConnectionWeight::SetParamPointer(double *param_ptr, int idx) {
 }
 
 double ConnectionWeight::eval() {
-    return this->weight_function(this->param_ptrs, this->n_params);
+    return (*this->weight_function)(this->param_ptrs, this->n_params);
 }
\ No newline at end of file
diff --git a/src/NetConnection/ConnectionWeight.h b/src/NetConnection/ConnectionWeight.h
index 295549ab..c961324a 100644
--- a/src/NetConnection/ConnectionWeight.h
+++ b/src/NetConnection/ConnectionWeight.h
@@ -25,7 +25,7 @@ protected:
     /**
      *
      */
-    std::function<double(double **, int)> weight_function;
+    std::function<double(double **, int)> *weight_function = nullptr;
 
 public:
 
@@ -39,7 +39,7 @@ public:
      * @param param_count
      * @param f
      */
-    ConnectionWeight(int param_count, std::function<double(double **, int)> f);
+    ConnectionWeight(int param_count, std::function<double(double **, int)> *f);
 
     /**
      *
diff --git a/src/NetConnection/ConnectionWeightIdentity.h b/src/NetConnection/ConnectionWeightIdentity.h
index e121ddf7..f2e26f40 100644
--- a/src/NetConnection/ConnectionWeightIdentity.h
+++ b/src/NetConnection/ConnectionWeightIdentity.h
@@ -19,8 +19,15 @@ class ConnectionWeightIdentity:public ConnectionWeight {
 private:
 public:
 
+    /**
+     *
+     */
     ConnectionWeightIdentity();
 
+    /**
+     *
+     * @return
+     */
     double eval() override;
 };
 
diff --git a/src/Network/NeuralNetwork.cpp b/src/Network/NeuralNetwork.cpp
index a5090d0a..ffa3354d 100644
--- a/src/Network/NeuralNetwork.cpp
+++ b/src/Network/NeuralNetwork.cpp
@@ -6,9 +6,11 @@
  */
 
 #include "NeuralNetwork.h"
+#include "../NetConnection/ConnectionWeightIdentity.h"
 
 NeuralNetwork::NeuralNetwork() {
     this->neurons = new std::vector<Neuron*>(0);
+    this->connection_weights = new std::vector<double>(0);
 }
 
 NeuralNetwork::~NeuralNetwork() {
@@ -28,11 +30,63 @@ NeuralNetwork::~NeuralNetwork() {
         delete this->active_eval_set;
         this->active_eval_set = nullptr;
     }
+    if(this->connection_weights){
+        delete this->connection_weights;
+        this->connection_weights = nullptr;
+    }
 }
 
-void NeuralNetwork::add_neuron(Neuron *n) {
+int NeuralNetwork::add_neuron(Neuron *n) {
     this->neurons->push_back(n);
     this->in_out_determined = false;
+
+    return (int)this->neurons->size() - 1;
+}
+
+void NeuralNetwork::add_connection_simple(int n1_idx, int n2_idx, int weight_idx, double weight_value) {
+
+    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();
+    con_weight_u1u2->SetParamPointer(&this->connection_weights->at(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)> *f, int* weight_indices, double* weight_values, int n_weights) {
+
+    ConnectionWeight *con_weight_u1u2 = new ConnectionWeight(n_weights, f);
+    //we analyze weights
+    int weight_idx = 0;
+    double weight_value = 0.0;
+    for(int 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->SetParamPointer(&this->connection_weights->at(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);
 }
 
 void NeuralNetwork::determine_inputs_outputs() {
diff --git a/src/Network/NeuralNetwork.h b/src/Network/NeuralNetwork.h
index d6acc945..cb77c595 100644
--- a/src/Network/NeuralNetwork.h
+++ b/src/Network/NeuralNetwork.h
@@ -42,6 +42,8 @@ private:
       */
      std::vector<Neuron*>* output_neurons = nullptr;
 
+     std::vector<double>* connection_weights = nullptr;
+
      /**
       *
       */
@@ -86,9 +88,30 @@ public:
 
     /**
      *
-     * @param n
+     * @param[in] n
+     * @return
+     */
+    int add_neuron(Neuron* n);
+
+    /**
+     *
+     * @param[in] n1_idx
+     * @param[in] n2_idx
+     * @param[in] weight_idx
+     * @param[in] weight_value
+     */
+    void add_connection_simple(int n1_idx, int n2_idx, int weight_idx, double weight_value);
+
+    /**
+     *
+     * @param n1_idx
+     * @param n2_idx
+     * @param f
+     * @param weight_indices
+     * @param weight_values
+     * @param n_weights
      */
-    void add_neuron(Neuron* n);
+    void add_connection_general(int n1_idx, int n2_idx, std::function<double(double **, int)> *f, int* weight_indices, double* weight_values, int n_weights);
 
 
 
diff --git a/src/Neuron/Neuron.cpp b/src/Neuron/Neuron.cpp
index bb074f84..22afd530 100644
--- a/src/Neuron/Neuron.cpp
+++ b/src/Neuron/Neuron.cpp
@@ -8,6 +8,9 @@ Neuron::~Neuron() {
     }
 
     if(this->edges_out){
+        for(auto *edge: *this->edges_out){
+            delete edge;
+        }
         delete this->edges_out;
         this->edges_out = nullptr;
     }
diff --git a/src/main.cpp b/src/main.cpp
index 4e601007..483fdab0 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -15,6 +15,7 @@
 
 #include "NetConnection/ConnectionWeightIdentity.h"
 
+//TODO prepsat tak, aby neuronova sit managovala destruktory vsech potrebnych objektu (kvuli serializaci)
 /**
  * Test of simple neural network
  * Network should evaluate the function f(x) = x + 1
@@ -22,28 +23,29 @@
 void test1(){
     std::vector<double> in(1);
     std::vector<double> out(1);
-    NeuralNetwork net;
-
-    NeuronLinear u1(1.0, 1.0); //f(x) = x + 1.0
-    NeuronLinear u2(0.0, 1.0); //f(x) = x
-
-    auto * parameters = new double[1];
-    parameters[0] = 1.0;
-
-//    ConnectionWeight con_weight_u1u2(1, [](double ** params, int n_params){ return (*params[0]);});
-    ConnectionWeightIdentity con_weight_u1u2;
-
-//    con_weight_u1u2.SetParamPointer(&parameters[0], 0);
-    con_weight_u1u2.SetParamPointer(&parameters);
-
-
-    Connection u1u2(&u1, &u2, &con_weight_u1u2);
 
-    u1.add_connection_out(&u1u2);
-    u2.add_connection_in(&u1u2);
-
-    net.add_neuron(&u1);
-    net.add_neuron(&u2);
+    NeuralNetwork net;
+    NeuronLinear* u1 = new NeuronLinear(1.0, 1.0); //f(x) = x + 1.0
+    NeuronLinear* u2 = new NeuronLinear(0.0, 1.0); //f(x) = x
+    int idx1 = net.add_neuron(u1);
+    int idx2 = net.add_neuron(u2);
+////////////////////// SIMPLE EDGE WEIGHT ////////////////////////////////////////
+//    net.add_connection_simple(idx1, idx2, -1, 1.0);
+////////////////////// END SIMPLE EDGE WEIGHT ////////////////////////////////////////
+
+/////////////////////////BEGIN OF COMPLEX EDGE WEIGHT//////////////////////////////
+//TODO vyresit memleak
+   std::function<double(double **, int)> weight_function = [](double ** params, int n_params){
+        //w(x, y) = x + y
+        double a = (*(params[0]));
+        double b = (*(params[1]));
+        printf("eval: %f, %f\n",  a, b);
+        return (a + 0.0 * b);
+    };
+    int  weight_indices [2]= {0, -1};
+    double weight_values [2] = {1.0, 5.0};
+    net.add_connection_general(idx1, idx2, &weight_function, weight_indices, weight_values, 2);
+/////////////////////////END OF COMPLEX EDGE WEIGHT//////////////////////////////
     for(int i = 0; i < 20; ++i){
         in[0] = 0.05 * i;
         net.eval_single(in, out);
@@ -51,11 +53,9 @@ void test1(){
         printf("x = %3.2f, f(x) = %3.2f, expected output = %3.2f\n", in[0], out[0], in[0] + 1.0);
     }
 
-
-
-
-
-    delete [] parameters;
+    //clean-up phase
+    delete u1;
+    delete u2;
 }
 
 int main(int argc, char** argv){
-- 
GitLab