As you've probably noticed machine learning is a really hot topic nowadays.
And part of the machine learning mechanics is the use of neuro networks so that's where we're going
to take a look at.
In this demonstration of the composite design pattern.
So to begin with what I'm going to do is I'm going to define an interface for a neuron or I actually
define a single neuron as a structure so we're going to have a structure called neuron and a neuron
has connections to other neurons which we're going to model as vectors of pointers.
So I'll have a vector of neuron pointers for both the incoming connections as well as the outgoing connections.
We'll also have let's say each neuron will have its own identifier so I'll have an unsigned ID here
and we'll have a constructor which actually initializes the ID based on some static value that we keep
on incrementing so I'll have a static int ID with an initial value of 1 and I will set the ID to just
ID plus.
So I'll keep incrementing it on each invocation of the constructor and so each year on this is going
to have its own unique ID.
Now the one thing that we want from a neuron is we want to be able to connect then you're on to some
other neuron that we can write a utility function for that.
So here I'll have a function called connect to and this can connect to another neuron which I'll have
as a reference so here is the other neuron and remember you have to add this neuron to your outgoing
connections and you have to add yourself to the neurons incoming connections so we would say our dog
pushed back pushed back a pointer to other and then we would say other dogs in dog push back and this
this is how you can connect two neurons together and we can actually print this whole thing out so we
can write some sort of utility stream operator for actually showing the whole thing.
Let me do that so we can generate the stream output operator but none of this stuff is really going
to give us a particularly nice implementation.
So what I'm going to do instead is I'll go through each of the neuron pointers in the inputs and then
I would simply output them as sort of outputting the ID and then basically just showing that they go
to some other neurons so let's put a tab here an arrow and then another tab and I will have the object
id here and we will have actually it's not object it's called neuron.
So let me just generalize it to be like this it certainly helps quite a bit and then close the square
brackets so we're going to kind of have the square brackets here and put an end line at the end of it.
So these are the incoming connections and then for the outgoing connections it's a very similar kind
of setup.
So let me just copy this.
So in this case we're going to be you use OPG out.
But apart from that well the ordering is actually different.
So let's actually implemented this so we're going to put a square bracket in here we'll put the object
id in here and then we'll close it and do that tab.
And then so the arrow and another tab and then we'll put the wrong ID the the one is our outgoing connection
and lets put an end line at the end of adult.
So here we go we have a scream out by the operator and this allows us to basically print some of the
information about a particular neuron so if I make two neurons and connect them together we can actually
get to see something on the screen so let's have a neuron and one and two and I will say and one connect
two and two and we can actually print this so we can see out for example and 1 followed by two and sure
if I need that and line I think they all have their implicit headlines.
But anyways let's just have one anyway.
OK.
So this is a very basic implementation and it works just fine so long as you just have individual neurons.
Now the one thing that we haven't accounted for is what happens when you have more than one yura.
But before we do that let's actually take a look at what's going on here in terms of the Eris OK so
this output stream I'm going to delete it from here and actually stick it somewhere in here.
That's much better.
I have special macros here which helped me navigate the problems with poost under the Microsoft compiler.
So we have to have this fix and hopefully a second time round were actually going to have some output
and some information about the incoming and outgoing connections for this neuron.
So here is your number one and you can see that it has an outgoing connection to two.
And in the same way to has an incoming connection from one so the connection is fine.
The connection there is no particular problem so far.
Now here is where the composite design pattern comes in.
Let's suppose that we decide to make a neuron layer and now a layer of neurons is basically like a collection.
So instead of having a single neuron you just make a pack of them you make for example 10 of them or
how many you want and you can keep them in something like a vector.
In actual fact what I'm going to do here is a cardinal sin so I'll make an your on later and I'll actually
inherit from a vector of neurons.
Now typically you're not supposed to inherit from SDL types such as vector because they don't have all
the structures and you could run into trouble.
But in this particular case I'm going to let this one slide simply because it's a fasted demonstration
So here what we want to do is we want to make in your on layer and given a certain number of neurons
so we have the count and then we can go a while count goes to zero.
We can place back a brand new neuron neuron though that's all that in your own layer does.
And we can of course have a stream output operators of all the prints each one of the individual in
your hands.
So here I would do a stream output operator once again.
I'm not going to call it les or I really like the fact that C line deduces the names but I wanted to
be called RBJ so that it's kind of more streamlined if I decide to change something later on.
So for auto and in LBJ I'm just going to go through every single element and output it as it is and
then I will return the output stream.
So that is my implementation of a neuron layer.
But now I have a serious problem because guess what I want to be able to connect every single neuron
from a neuron layer to a neuron.
And furthermore actually several scenarios where I want to do this kind of thing because essentially
what I want to do and we already have that code for connecting a single neuron to another neuron like
here.
So if I make in your own lair let's call it an owl for example.
Now actually let's let's let's have it let's have player.
They are one for example with a certain number of elements.
And let's have a layer or two that's actually give them full names because the L looks fat.
Layer one with two elements and layer two with let's say three elements.
Now I have four distinct scenarios for how I want to connect the neurons together so I can connect a
neuron to your own.
We've done that already so that scenario number one.
And then I might want to connect to a neuron to a layer like so I might want to connect a layer or something
like To this layer connected to a neuron and I might want to also connect a layer or two layers so I
might want to say layer one that connect to a late year two like so.
Now as you probably guessed if you were to implement this head on you're going to have a total of four
different functions.
So once again this is this state space explosion thing and it's not good it's not good because we we
want a single function we want both the layer as well as individual neurons to somehow be enumerable.
So let me show you how you can actually have your cake and eat it how you can in fact get this this
kind of approach where you have just a single function.
And yes it does involve introducing a new interface that both of these types are going to conform to.
So here is the interface and I'm going to be using something called CRT P which is the curiously recurring
template pattern you're going to see why we need it in just a second.
But basically I'm going to give both neuron and neuron layer a base class which is going to allow them
to connect to one another through just one function just a single function no more than one.
So here we go.
I'm going to have a template struct which will have type name self and is going to be called some neurons.
So I think we can all agree that both a neuron and then your own layer represent some neurons is just
the case that the neuron represents a single neuron.
And then you are on there represents a bunch of them.
So here we going to have a template version with type named T of the Connect to function connect to
other that.
So this is our interface this is our function it's actually should be void.
Let's stick to the void in here.
So this is the function of which will connect any kind of some neurons object to any kind of object.
So what we need to do is we need to enumerate our neurons and we need to enumerate then your odds of
the other argument.
Now the way we enumerate ourselves is by first of all figuring out what our type is.
And the only way we can figure out what archetype is is through RTP and that's why we have the self
template argument here.
So when I want to enumerate my own neurons I would write the following.
I would say for Meuron reference from Ian and here comes the tricky part.
So right now our own type our own this pointer points to some neurons which isn't particularly helpful
because we want to be able to sort of iterate the concrete type.
So what we do is we have a static cast where we cast that this pointer to a self pointer.
And of course what we do then is we reference this pointer and this way we can enumerate the members
of whatever type we are in.
But we're doing it through the type preference of the correct type.
OK.
So that's the left part of the connection and then the right part of the connection is the neurons from
the other argument.
Now this is considerably easier because we just say for a neuron reference to in other to in other like
so and then we can connect them together and once again the connection code is exactly this code.
So let me actually cut the south from here and stick it in here so instead of out of course it's going
to be from from DOT about the push back and instead of either.
It's well actually this is going to be two.
So from that don't push back either.
And then to Daut end up push back.
And this has to be a from a point or two from.
So this is how you would implement a single connect to for every single object.
Now of course we're not done yet because we need to actually get this interface to propagate.
We need both in Iran as well as the neuron layer to actually inherit from this.
So we're going to have some neurons of neuron here and we're going to have here some neurons of neurons
there though.
So we've now implemented this mechanism everywhere.
Unfortunately it's still not going to work.
It's still not going to work and the reason why it's not going to work is because we don't yet know
how to support this for operators so we're using range based.
For in both of these but of course it's not going to work.
So it will certainly work on a neuron layer because in your own layer is a vector of neurons.
There's no particular problem here but it is going to be a problem for a single neuron because it doesn't
really make sense to have it as part of a range base for loop.
It simply doesn't make any sense and so if we try to compile it let me actually just go and build this.
We are going to see some problems here because well this is not allowed.
And you know we want the composite design pattern to work.
So here is the problem.
If we just.
Well once again I seem to have included a header which isn't even supposed to be here.
So let me try to go again because I want to see the errors pertaining to our particular case.
I do know that there are in fact errors.
But we need to be able to see them.
There we go.
OK so here is the problem you can see it here on the right so there is no callable begin function found
in type Iran.
So this is a very useful error message because it tells us what we need to do and we are going to fix
it right now.
So we want the neuron to have begin and end functions.
We wanted to act as a collection returning itself because that's the only element it's a scalar element
which is posing as a collection of elements.
So how do we do this.
Well in actual fact it's very easy.
If I decide to implement a begin function all I would do is return this.
So I would return the pointer to this particular element and then for the end function for the end function
I would return this plus one.
So this plus one seems like a very crude way of actually figuring out where the thing should end.
But on the other hand what's going to happen in a range based for loop.
It will go from begin.
It will get the beginning point and then that will do plus plus on it and it we'll hit this plus one
and it will compare it to the end.
It will figure out it's reached the end and that's going to be the only element.
So essentially if we now go ahead and we build this this should actually build So as you can see the
application compiles and we can actually run this.
Now I've added a lot of code here for interconnecting all the different parts so we're going to see
lots and lots of output here especially if I decide to for example see out layer 1.
Let's take a look at what this gives us.
But essentially this code works.
It works and it works through just a single function which is added to the base class so here is the
layer as you can see the connections to the individual neurons.
But this this essentially works without any problems and the reason for that is that we've managed to
get a scalar object so a singer object we managed to get it to masquerade as if it were a collection
albeit a collection of a single element.
And this allows us to write very nice generic definition such as the one that you see here and we use
the CRC P trick to make sure that the type that we're passing in the time that we're using as we implement
the some neurons interface is actually being used for a static class so that we're iterating the right
kind of collection.