Lecture thumbnail 0:00 / 0:00 As you probably know, machine learning is all the rage nowadays in one component of machine learning

is neural networks.

So we’re going to take a look at how the composite design pattern can show up in the simple simulation

of neurons and neuron layers and how they are connected.

So let’s suppose I make a class called Neuron.

Now what I want to do is I want to have some sort of value for this neuron, so I might as well make

a public value.

I’m not going to be using it though, because what I want is I want the connection from this neuron

to other neurons.

And I also want to track the connections which are incoming.

So we have two sets of connections, connections coming in and connections going out.

So for this I can make two lists so I can have a list of neuron, list of neuron, and I’ll have to

list one calling in and the other called out.

So this is very simple.

You have lots and lots of neurons.

They are all connected to one another.

And typically the way that you connect one neuron to another is something like the following.

So you have a method called connect to and this method connects one neuron to another.

So you have neuron other.

And then of course you add this neuron to your outgoing connection.

So you say out dot, add other.

And then on the other neuron you add the incoming connection.

So you say other dot n dot add this.

Okay, so this is how you connect a single neuron to some other neuron so we can make them.

So I can say var neuron one equals new neuron.

There we go.

And I can make neuron two as well.

And for now everything is fantastic because I can say neuron one neuron, one dot connect to neuron

two and everything is fine.

I mean, this is completely legitimate code.

Everything is correct.

It’s working perfectly.

Now I’m going to throw the spanner in the works here.

What I’m going to do is I’m going to add a neuron layer.

Now a neuron layer is quite simply a collection of neurons.

It’s a collection of neurons.

And the thing about this collection is you also want it to be connectable to things.

So I’m going to make a new class called Neuron Layer, and here I’m going to implement a collection

of neuron.

This half collection of neurons.

Okay.

Could have been a list or something else.

But, you know, let’s keep it as collection.

And now we have a very big problem because now the connectivity is still required.

So if I have also a bunch of layers.

So if I have layer one equals new neuron layer, and if I have layer two, I have the requirements that

I can connect a neuron to a neuron that’s already handled a neuron to a layer, a layer to a neuron

or a layer to a layer.

So instead of just one method like we have here, we now need, well, it looks like we need four separate

methods to actually connect every kind of object to any other kind of object, because remember, a

neuron layer is just a bunch of neurons.

So we want to take every neuron, for example, from a layer and connect it to some other neuron.

But we cannot because we don’t have the API for this and we can certainly start writing for each loops

and whatever.

But imagine that later on, somewhere down the line you have a class called Neuron Ring, which also

does something different.

It’s not a collection of neurons.

It could be like a list of neuron or something, and then you’re in trouble once again because then

the explosion of this number of different methods, different permutations is too much.

And you don’t want to deal with this.

You want to apply the composite pattern.

But the question is how do you apply it?

Now, I’m going to surprise you here and tell you that you can keep a scenario where you have just a

single method covering all of these.

Now, the way that you do this is by using an extension method.

And the question is, well, an extension method on what?

What is in common between a neuron which has no base classes and is just a stand alone class and the

neuron layer whose base class is already occupied.

So it’s not like we can give both of these classes a base class.

We cannot do it because this class already has a base class.

So what do we do?

And the answer is that we simply treat both neuron layer and neuron as a collection of neurons.

In the case of a neuron layer, it’s obviously a collection already, but in the case of a neuron,

we can also treat it as a collection of just a single element.

So the question is, well, what do we do?

Do we have to inherit from collection of neuron here in a single neuron?

And the answer is no, you don’t have to do this because essentially in the net framework, we already

have a paradigm, we already have an interface for expressing the idea of enumeration.

And that is, of course I innumerable.

Now in the case of a neuron layer, it’s a collection, so it already implements enumerable, no problem

here.

In the case of a neuron, we simply implement a enumerable like this.

So we say a neuron is an i enumerable of neuron.

And then of course we have to implement the missing members.

They get enumerator both of these varieties.

And here the default implementation is just to get the strongly typed variety.

So we only have to implement one method.

Now typically in Getenumerator is where you return each of the containing elements.

But here we have a scalar, so a neuron is a scalar value and we want to represent it as an enumerable.

So the only way this can be done is by yield return this.

So we return ourselves as the one and only element which is actually providing an enumerator.

So this is great because now a neuron is an enumerable and the neuron layer is also an enumerable.

And what this means is we can now write an extension method on Enumerables.

So here we’ll have a static class called extension methods, and I’m going to have a public static void

connect to.

So instead of having connect to down here for down here rather for a single neuron, I’m going to get

rid of this.

We’re going to implement this for any two collections of neurons.

So we have the left hand side, this enumerable of neuron, let’s call it self.

And then we have enumerable enum variable of neuron, other as the right hand side.

And what we do is we iterate through all of the neurons in the self, all of the neurons in the other,

and we connect them together.

So we say, well, first of all we check for referential equality because they’re very equal.

We don’t have to do anything.

So if reference equals self and other then simply return.

But if not, what we do is we say for each var from in self, for each var to in other, and this is

where we can actually do some work.

So we say from dot out, dot add to and then to dot in dot add from there we go.

The indentation is a bit broken here, but here is the implementation which will work in all of these

scenarios.

So what you’ll see here is that we now have a way of connecting, let’s say.

Neuron to a layer neuron one.

Connect to.

Layer one.

This is completely legal now because both of these both neuron one and layer one are innumerable of

neuron because essentially a single scalar neuron is masquerading as an enumeration by yield returning

itself.

Yield returning this.

And of course we can connect layers to layers, for example, so we can say layer one dot connect to

layer two.

So as you can see with the composite design pattern, once again, you can make sure that even scalar

values are treated as composite values with singular content.

And that way you can write very generalized algorithms on both singular as well as composite values.

Play Play Stop Play Start Play information alert