func (n *Network) restoreSync(p *Pattern) (*Pattern, error) {
	p.Vec().MulVec(n.weights, p.Vec())
	for i := 0; i < p.Len(); i++ {
		if p.At(i) >= n.bias.At(i, 0) {
			p.RawData()[i] = 1.0
		} else {
			p.RawData()[i] = -1.0
		}
	}
	return p, nil
}
restoreSync restores patterns from the network synchronously
package hopfield
import (
	"fmt"
	"math"
	"math/rand"
	"strings"
	"gonum.org/v1/gonum/mat"
)
type Network struct {
	weights *mat.SymDense
	bias *mat.VecDense
	method string
	memorised int
}
func NewNetwork(size int, method string) (*Network, error) {
	if size <= 0 {
		return nil, fmt.Errorf("invalid network size: %d", size)
	}
	if !strings.EqualFold("hebbian", method) && !strings.EqualFold("storkey", method) {
		return nil, fmt.Errorf("unsupported training method: %s", method)
	}
	weights := mat.NewSymDense(size, nil)
	bias := mat.NewVecDense(size, nil)
	return &Network{
		weights: weights,
		bias:    bias,
		method:  method,
	}, nil
}
func (n Network) Weights() mat.Matrix {
	return n.weights
}
func (n Network) Bias() mat.Matrix {
	return n.bias
}
func (n Network) Capacity() int {
	_, c := n.weights.Dims()
	if strings.EqualFold("storkey", n.method) {
		return int(math.Floor(float64(c) / (2 * math.Sqrt(math.Log(float64(c))))))
	}
	return int(math.Floor(float64(c) / (2 * math.Log(float64(c)))))
}
func (n Network) Memorised() int {
	return n.memorised
}
func (n *Network) Store(patterns []*Pattern) error {
	if len(patterns) == 0 {
		return fmt.Errorf("invalid patterns supplied: %v", patterns)
	}
	_, nCount := n.weights.Dims()
	for _, p := range patterns {
		if p == nil {
			return fmt.Errorf("invalid pattern supplied: %v", p)
		}
		if p.Len() != nCount {
			return fmt.Errorf("invalid pattern dimension: %d", p.Len())
		}
	}
	switch n.method {
	case "hebbian":
		n.storeHebbian(patterns)
	case "storkey":
		n.storeStorkey(patterns)
	}
	return nil
}
func (n *Network) Restore(p *Pattern, mode string, iters int) (*Pattern, error) {
	if p == nil {
		return nil, fmt.Errorf("invalid pattern supplied: %v", p)
	}
	_, nCount := n.weights.Dims()
	if p.Len() != nCount {
		return nil, fmt.Errorf("invalid pattern dimension: %v", p.Len())
	}
	if strings.EqualFold("async", mode) && iters <= 0 {
		return nil, fmt.Errorf("invalid number of iterations: %d", iters)
	}
	switch mode {
	case "sync":
		return n.restoreSync(p)
	case "async":
		return n.restoreAsync(p, iters)
	}
	return nil, fmt.Errorf("unsupported mode: %s", mode)
}
func (n Network) Energy(p *Pattern) (float64, error) {
	if p == nil {
		return 0.0, fmt.Errorf("invalid pattern supplied: %v", p)
	}
	_, nCount := n.weights.Dims()
	if p.Len() != nCount {
		return 0.0, fmt.Errorf("invalid pattern dimension: %v", p.Len())
	}
	energy := -0.5 * mat.Inner(p.Vec(), n.weights, p.Vec())
	energy += mat.Dot(n.bias, p.Vec())
	return energy, nil
}
func (n *Network) storeHebbian(patterns []*Pattern) {
	dim := patterns[0].Len()
	w := mat.NewSymDense(dim, nil)
	for i := 0; i < dim; i++ {
		for j := i + 1; j < dim; j++ {
			for _, p := range patterns {
				w.SetSym(i, j, w.At(i, j)+(p.At(i)*p.At(j)/float64(dim)))
			}
		}
	}
	n.weights.AddSym(n.weights, w)
}
func (n *Network) storeStorkey(patterns []*Pattern) {
	dim := patterns[0].Len()
	w := mat.NewSymDense(dim, nil)
	var sum float64
	for i := 0; i < dim; i++ {
		for j := i + 1; j < dim; j++ {
			for _, p := range patterns {
				sum = p.At(i) * p.At(j)
				sum -= p.At(i) * localField(w, p, j, i)
				sum -= p.At(j) * localField(w, p, i, j)
				sum *= 1 / float64(dim)
				w.SetSym(i, j, w.At(i, j)+sum)
			}
		}
	}
	n.weights.AddSym(n.weights, w)
}
func localField(w *mat.SymDense, p *Pattern, i, j int) float64 {
	sum := 0.0
	for k := 0; k < p.Len(); k++ {
		if k != i && k != j {
			sum += w.At(i, k) * p.At(k)
		}
	}
	return sum
}