colgreen / sharpneat

SharpNEAT - Evolution of Neural Networks. A C# .NET Framework.
https://sharpneat.sourceforge.io/
Other
375 stars 96 forks source link

Attempt to decode a cyclic network into a FastAcyclicNetwork #15

Closed ozyman42 closed 7 years ago

ozyman42 commented 7 years ago

This seems to cause the program to stop due to a StackOverflowException.

colgreen commented 7 years ago

The short response is: don't do that! I.e. use FastCyclicNetworkFactory for cyclic networks.

However I'm going to leave this open as a reminder to come back to this. The situation is as follows...

FastAcyclicNetworkFactory.CreateFastAcyclicNetwork() contains a Debug.Assert() statement to catch cyclic networks, however this is only gets invoked for a debug compile output. This is by design, primarily because it's computationally expensive to do the test (we have to traverse the network structure), and also it's largely unnecessary since this code should never be passed a cyclic network.

However, all of the relevant code is being re-written as part of a major refactor, and as part of that I think it would be an improvement if the acyclic decoder detected cyclic networks (and threw an exception) as part of the decode process, rather than relying on a completely separate (and expensive) cyclicity test.

colgreen commented 7 years ago

On second thoughts, having looked at the code I'm comfortable with this code causing a stack overflow. To detect a cycle requires tracking all of the ancestor nodes on a traversal path, which is an unnecessary computational cost. So I think I will close this issue, pending any feedback that might convince me otherwise.

ozyman42 commented 7 years ago

Where can I set in the experiment that the network is supposed to be acyclic?

colgreen commented 7 years ago

Take a look at one of the existing implementations of IGuiNeatExperiment, e.g. in BinaryElevenMultiplexerExperiment:

Line 127:

     _activationScheme = ExperimentUtils.CreateActivationScheme(xmlConfig, "Activation");

Line 135:

     _neatGenomeParams = new NeatGenomeParameters();
     _neatGenomeParams.FeedforwardOnly = _activationScheme.AcyclicNetwork;

Ultimately that parameters object makes its way into the genome factory so that the evolution code knows to avoid making any connections that would form a cycle in the connectivity graph:

Line 173:

    return new NeatGenomeFactory(InputCount, OutputCount, _neatGenomeParams);

The activation scheme object it also passed to the genome decoder, so that it knows which of the two decoder routines to use (cyclic or acyclic):

Line 164:

     return new NeatGenomeDecoder(_activationScheme);
ozyman42 commented 7 years ago

Thank you