func (s *FastModularNetworkSolver) Relax(maxSteps int, maxAllowedSignalDelta float64) (relaxed bool, err error) {
	for i := 0; i < maxSteps; i++ {
		if relaxed, err = s.forwardStep(maxAllowedSignalDelta); err != nil {
			return false, err
		} else if relaxed {
			break  
		}
	}
	return relaxed, nil
}
Relax Attempts to relax network given amount of steps until giving up. The network considered relaxed when absolute
value of the change at any given point is less than maxAllowedSignalDelta during activation waves propagation.
If maxAllowedSignalDelta value is less than or equal to 0, the method will return true without checking for relaxation.
package network
import (
	"errors"
	"fmt"
	neatmath "github.com/yaricom/goNEAT/v2/neat/math"
	"math"
)
type FastNetworkLink struct {
	SourceIndex int
	TargetIndex int
	Weight float64
	Signal float64
}
type FastControlNode struct {
	ActivationType neatmath.NodeActivationType
	InputIndexes []int
	OutputIndexes []int
}
type FastModularNetworkSolver struct {
	Id int
	Name string
	neuronSignals []float64
	neuronSignalsBeingProcessed []float64
	activationFunctions []neatmath.NodeActivationType
	biasList []float64
	modules []*FastControlNode
	connections []*FastNetworkLink
	inputNeuronCount int
	sensorNeuronCount int
	outputNeuronCount int
	biasNeuronCount int
	totalNeuronCount int
	activated []bool
	inActivation []bool
	lastActivation []float64
	adjacentList [][]int
	reverseAdjacentList [][]int
	adjacentMatrix [][]float64
}
func NewFastModularNetworkSolver(biasNeuronCount, inputNeuronCount, outputNeuronCount, totalNeuronCount int,
	activationFunctions []neatmath.NodeActivationType, connections []*FastNetworkLink,
	biasList []float64, modules []*FastControlNode) *FastModularNetworkSolver {
	fmm := FastModularNetworkSolver{
		biasNeuronCount:     biasNeuronCount,
		inputNeuronCount:    inputNeuronCount,
		sensorNeuronCount:   biasNeuronCount + inputNeuronCount,
		outputNeuronCount:   outputNeuronCount,
		totalNeuronCount:    totalNeuronCount,
		activationFunctions: activationFunctions,
		biasList:            biasList,
		modules:             modules,
		connections:         connections,
	}
	fmm.neuronSignals = make([]float64, totalNeuronCount)
	fmm.neuronSignalsBeingProcessed = make([]float64, totalNeuronCount)
	for i := 0; i < biasNeuronCount; i++ {
		fmm.neuronSignals[i] = 1.0  
	}
	fmm.activated = make([]bool, totalNeuronCount)
	fmm.inActivation = make([]bool, totalNeuronCount)
	fmm.lastActivation = make([]float64, totalNeuronCount)
	fmm.adjacentList = make([][]int, totalNeuronCount)
	fmm.reverseAdjacentList = make([][]int, totalNeuronCount)
	fmm.adjacentMatrix = make([][]float64, totalNeuronCount)
	for i := 0; i < totalNeuronCount; i++ {
		fmm.adjacentList[i] = make([]int, 0)
		fmm.reverseAdjacentList[i] = make([]int, 0)
		fmm.adjacentMatrix[i] = make([]float64, totalNeuronCount)
	}
	for i := 0; i < len(connections); i++ {
		crs := connections[i].SourceIndex
		crt := connections[i].TargetIndex
		fmm.adjacentList[crs] = append(fmm.adjacentList[crs], crt)
		fmm.reverseAdjacentList[crt] = append(fmm.reverseAdjacentList[crt], crs)
		fmm.adjacentMatrix[crs][crt] = connections[i].Weight
	}
	return &fmm
}
func (s *FastModularNetworkSolver) ForwardSteps(steps int) (res bool, err error) {
	for i := 0; i < steps; i++ {
		if res, err = s.forwardStep(0); err != nil {
			return false, err
		}
	}
	return res, nil
}
func (s *FastModularNetworkSolver) RecursiveSteps() (res bool, err error) {
	if len(s.modules) > 0 {
		return false, errors.New("recursive activation can not be used for network with defined modules")
	}
	for i := 0; i < s.totalNeuronCount; i++ {
		s.activated[i] = i < s.sensorNeuronCount
		s.inActivation[i] = false
		if i >= s.sensorNeuronCount {
			s.lastActivation[i] = s.neuronSignals[i]
		}
	}
	for i := 0; i < s.outputNeuronCount; i++ {
		index := s.sensorNeuronCount + i
		if res, err = s.recursiveActivateNode(index); err != nil {
			return false, err
		} else if !res {
			return false, fmt.Errorf("failed to recursively activate the output neuron at %d", index)
		}
	}
	return res, nil
}
func (s *FastModularNetworkSolver) recursiveActivateNode(currentNode int) (res bool, err error) {
	if s.activated[currentNode] {
		s.inActivation[currentNode] = false
		return true, nil
	}
	s.inActivation[currentNode] = true
	s.neuronSignalsBeingProcessed[currentNode] = 0
	for i := 0; i < len(s.reverseAdjacentList[currentNode]); i++ {
		currentAdjNode := s.reverseAdjacentList[currentNode][i]
		if s.inActivation[currentAdjNode] {
			s.neuronSignalsBeingProcessed[currentNode] += s.lastActivation[currentAdjNode] * s.adjacentMatrix[currentAdjNode][currentNode]
		} else {
			if !s.activated[currentAdjNode] {
				res, err = s.recursiveActivateNode(currentAdjNode)
				if err != nil {
					return false, err
				} else if !res {
					return false, fmt.Errorf("failed to recursively activate neuron at %d", currentAdjNode)
				}
			}
			s.neuronSignalsBeingProcessed[currentNode] += s.neuronSignals[currentAdjNode] * s.adjacentMatrix[currentAdjNode][currentNode]
		}
	}
	s.activated[currentNode] = true
	s.inActivation[currentNode] = false
	if s.neuronSignals[currentNode], err = neatmath.NodeActivators.ActivateByType(
		s.neuronSignalsBeingProcessed[currentNode], nil,
		s.activationFunctions[currentNode]); err != nil {
		res = false
	} else {
		res = true
	}
	return res, err
}