func (n *Network) IsControlNode(nid int) bool {
	for _, cn := range n.controlNodes {
		if cn.Id == nid {
			return true
		}
	}
	return false
}
IsControlNode is to check if specified node ID is a control node
package network
import (
	"bytes"
	"errors"
	"fmt"
	"github.com/yaricom/goNEAT/v2/neat/math"
	"gonum.org/v1/gonum/graph/path"
	"io"
)
type Network struct {
	Id int
	Name string
	Outputs []*NNode
	numLinks int
	allNodes []*NNode
	inputs []*NNode
	controlNodes []*NNode
	allNodesMIMO []*NNode
}
func NewNetwork(in, out, all []*NNode, netId int) *Network {
	n := Network{
		Id:           netId,
		inputs:       in,
		Outputs:      out,
		allNodes:     all,
		numLinks:     -1,
		allNodesMIMO: all,
	}
	return &n
}
func NewModularNetwork(in, out, all, control []*NNode, netId int) *Network {
	n := NewNetwork(in, out, all, netId)
	n.controlNodes = control
	n.allNodesMIMO = append(n.allNodesMIMO, control...)
	return n
}
func (n *Network) FastNetworkSolver() (Solver, error) {
	outputNeuronCount := len(n.Outputs)
	biasNeuronCount := 0
	inList := make([]*NNode, 0)
	biasList := make([]*NNode, 0)
	hiddenList := make([]*NNode, 0)
	for _, ne := range n.allNodes {
		switch ne.NeuronType {
		case BiasNeuron:
			biasNeuronCount += 1
			biasList = append(biasList, ne)
		case InputNeuron:
			inList = append(inList, ne)
		case HiddenNeuron:
			hiddenList = append(hiddenList, ne)
		}
	}
	inputNeuronCount := len(inList)
	totalNeuronCount := len(n.allNodes)
	activations := make([]math.NodeActivationType, totalNeuronCount)
	neuronLookup := make(map[int]int)  
	neuronIndex := processList(0, biasList, activations, neuronLookup)
	neuronIndex = processList(neuronIndex, inList, activations, neuronLookup)
	neuronIndex = processList(neuronIndex, n.Outputs, activations, neuronLookup)
	processList(neuronIndex, hiddenList, activations, neuronLookup)
	biases := make([]float64, totalNeuronCount)
	connections := make([]*FastNetworkLink, 0)
	if inConnects, err := n.processIncomingConnections(inList, biases, neuronLookup); err == nil {
		connections = append(connections, inConnects...)
	} else {
		return nil, err
	}
	if inConnects, err := n.processIncomingConnections(hiddenList, biases, neuronLookup); err == nil {
		connections = append(connections, inConnects...)
	} else {
		return nil, err
	}
	if inConnects, err := n.processIncomingConnections(n.Outputs, biases, neuronLookup); err == nil {
		connections = append(connections, inConnects...)
	} else {
		return nil, err
	}
	modules := make([]*FastControlNode, len(n.controlNodes))
	for i, cn := range n.controlNodes {
		inputs := make([]int, len(cn.Incoming))
		for j, in := range cn.Incoming {
			if inIndex, ok := neuronLookup[in.InNode.Id]; ok {
				inputs[j] = inIndex
			} else {
				return nil, fmt.Errorf("failed to lookup for input neuron with id: %d at control neuron: %d",
					in.InNode.Id, cn.Id)
			}
		}
		outputs := make([]int, len(cn.Outgoing))
		for j, out := range cn.Outgoing {
			if outIndex, ok := neuronLookup[out.OutNode.Id]; ok {
				outputs[j] = outIndex
			} else {
				return nil, fmt.Errorf("failed to lookup for output neuron with id: %d at control neuron: %d",
					out.InNode.Id, cn.Id)
			}
		}
		modules[i] = &FastControlNode{InputIndexes: inputs, OutputIndexes: outputs, ActivationType: cn.ActivationType}
	}
	return NewFastModularNetworkSolver(biasNeuronCount, inputNeuronCount, outputNeuronCount, totalNeuronCount,
		activations, connections, biases, modules), nil
}
func processList(startIndex int, nList []*NNode, activations []math.NodeActivationType, neuronLookup map[int]int) int {
	for _, ne := range nList {
		activations[startIndex] = ne.ActivationType
		neuronLookup[ne.Id] = startIndex
		startIndex += 1
	}
	return startIndex
}
func (n *Network) processIncomingConnections(nList []*NNode, biases []float64, neuronLookup map[int]int) ([]*FastNetworkLink, error) {
	connections := make([]*FastNetworkLink, 0)
	for _, ne := range nList {
		if targetIndex, ok := neuronLookup[ne.Id]; ok {
			for _, in := range ne.Incoming {
				if sourceIndex, ok := neuronLookup[in.InNode.Id]; ok {
					if in.InNode.NeuronType == BiasNeuron {
						biases[targetIndex] += in.ConnectionWeight
					} else {
						conn := FastNetworkLink{
							SourceIndex: sourceIndex,
							TargetIndex: targetIndex,
							Weight:      in.ConnectionWeight,
						}
						connections = append(connections, &conn)
					}
				} else {
					return nil, fmt.Errorf("failed to lookup for source neuron with id: %d", in.InNode.Id)
				}
			}
		} else {
			return nil, fmt.Errorf("failed to lookup for target neuron with id: %d", ne.Id)
		}
	}
	return connections, nil
}