0

Being a bit curious about machine learning, I have started reading some introductory tutorials related to that topic. Due to this, some days ago I found a very simple neural network example, implemented using Python with the numpy library and for practicing purposes, I decided to implement the same algorithm using C++ with as less as possible external libraries.

Then, I first coded a simple class able to handle matrix definitions/declarations and related mathematical operations like addition, multiplication, etc. I eloquently named that class as Matrix.
Here is its header file:

template <typename T>
class Matrix {
public:
  Matrix(int numRows, int numColumns);
  ~Matrix();
  int getRows();
  int getColumns();
  T readValue(int row, int column);
  void writeValue(int row, int column, T value);
  Matrix<T> operator+(Matrix<T> other);
  Matrix<T> operator-(Matrix<T> other);
  Matrix<T> operator*(T scalar);
  Matrix<T> operator*(Matrix<T> other);
  Matrix<T> entrywiseProduct(Matrix<T> other);
  Matrix<T> transpose();
  void print();
private:
  const int rows;
  const int columns;
  T** matrix;
};

As you can see, in order to allocate the right memory size, I decided to let the class constructor need two different arguments, that is number of rows and columns.

Anyway, after the definition of this briefly explained class, I started to code the main class for the network implementation. In particular, this more abstract class will mainly use the Matrix class for most of the operations. Here is the header:

#include "Matrix.h"

template <typename T>
class Neuron {
public:
  Neuron(int matrixRows, int matrixColumns);
  ~Neuron();
  Matrix<T> estimate(Matrix<T> inputMatrix);
  void train(Matrix<T> inputMatrix, Matrix<T> outputMatrix, int iterations);
private:
  Matrix<T> weights;
};

Despite it is not properly elegant, also this class constructor takes two input parameters related to matrices: that is because they will be used to correctly instantiate a matrix inside the class for the weighting coefficient storage.

And here is the issue: the mentioned matrix should be definitely instantiated after the initialisation of the Neuron class. As far as I know, this kind of operation requires the utilisation of a pointer which will be referred by the new function, used - on its side - to dynamically instantiate a Matrix class, in this case. However, on the other side, I have decided that operations with matrices always returns a new matrix and not a pointer to matrix, as you can see in the first class header.

So, I am going to ask you: is it possible to define a matrix inside the Neuron constructor and use it as a class variable, as defined in the previous header? In this way, I should be able to overwrite the same variable when doing operations on the weights named matrix.
If yes, how can I do this?

rudicangiotti
  • 566
  • 8
  • 20
  • 3
    Recommend a read of [What is the Rule of Three?](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) to prevent some nasty debugging in the future. While you're at it, familiarize yourself with its companions the [Rules of Zero and Five.](http://en.cppreference.com/w/cpp/language/rule_of_three) – user4581301 Feb 28 '18 at 02:41

1 Answers1

4

Yes; initialise your matrix like you should be initialising any other class member: in the constructor's member initialisation list.

Neuron::Neuron(int matrixRows, int matrixColumns)
   : weights(matrixRows, matrixColumns)
{}

When you do this:

T::T()
{
   member = firstValue;
}

that is not initialisation, it is assignment. You are correct when you say that this is insufficient if the member's type has no default constructor, because if you don't initialise a member yourself the compiler will try to initialise it for you, and it can't provide arguments it doesn't know about. The smart pointer/dynamic allocation is a common workaround if you can't properly initialise your member for some reason (for example, information needs to be gathered first).

By the way, T** suggests you're creating an array of pointers. Don't do that; it's very inefficient and overly complex. Just create an array of width×height Ts instead and map indexes accordingly (i = y×width + x; x = i % width; y = i / width).

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • I've sometimes used fields like `std::optional>` as an alternative workaround for cases where there was a more valid reason to defer initialization of the member, but still wanted its storage to be allocated within the parent object instead of on the heap. – Daniel Schepler Feb 28 '18 at 02:22
  • @DanielSchepler: Not bad. – Lightness Races in Orbit Feb 28 '18 at 02:23
  • @Lightness Races in Orbit, thank you so much for your useful suggestions. Anyway, I have updated now my question because something is going wrong. – rudicangiotti Feb 28 '18 at 17:14
  • @rudicangiotti: That's a completely different question. Please don't transform your questions. Post a new one. – Lightness Races in Orbit Feb 28 '18 at 17:21
  • @Lightness Races in Orbit, sorry for that but I thought it was a issue related to the topics of the question's first comment. Anyway, I am going to post a new question. – rudicangiotti Feb 28 '18 at 18:01