{-# LANGUAGE ParallelListComp #-}

-- | Performs experiments with two super attractors.
module Hopfield.Experiments.Experiment2SuperAttractors where

import Control.Monad (replicateM)
import Control.Monad.Random
import Test.QuickCheck
import Test.QuickCheck.Gen (unGen)

import Hopfield.Common
import Hopfield.Experiments.ExperimentUtil
import Hopfield.Hopfield (LearningType (..))
import Hopfield.Measurement
import Hopfield.SuperAttractors
import Hopfield.TestUtil (Type(H), patternGen)
import Hopfield.Util


genIO :: Gen a -> IO a
genIO g = do
    rndInt <- randomIO
    stdGen <- getStdGen
    return $ unGen g stdGen rndInt


basinHeader :: String
basinHeader = "Degree\tOrigin basin\tNew basin"


main :: IO ()
main = do

    let n          = 100    -- number of neurons
        numRandoms = 8      -- number of random patterns to include
        maxDegree  = 32     -- maximum degree of second super attractor
        fstDegree  = 8      -- (fixed) degree of first super attractor


    -- The first super attractor - primary care giver
    originPat <- genIO $ patternGen H n

    -- Sample random patterns with Hamming distance between 25-75% from origin
    -- This is to ensure that this is a pure super attractor experiment
    -- and not a cluster one!
    let minHamming = round $ n .* (0.25 :: Double)
        maxHamming = round $ n .* (0.75 :: Double)
        dist       = hammingDistribution n (minHamming, maxHamming)

    randomPats <- replicateM numRandoms $ sampleHammingRange originPat dist

    -- The second super attractor - retraining
    newPat <- sampleHammingRange originPat dist



    let pats        = originPat:newPat:randomPats
        originIndex = 0                         -- index of main pattern
        newIndex    = fstDegree + 1             -- index of new pattern
        degrees     = powersOfTwo maxDegree
        patCombiner = twoSuperAttrOneFixed fstDegree


    putStrLn $ unwords [show n, "neurons.", "Two Super attractors plus", show numRandoms, "random patterns.\n"]


    -- Check hamming distances
    doHamming originPat randomPats "origin" "random"
    doHamming newPat randomPats "new" "random"


    putStrLn "Building networks...\n"
    let nets = buildNetworks pats degrees Hebbian patCombiner


    --Check if patterns are fixed.
    putStrLn "Checking original pattern"
    doCheckFixed (zip degrees nets) originIndex "degrees"
    putStrLn "Checking new pattern"
    doCheckFixed (zip degrees nets) newIndex "degrees"


    putStrLn "Measuring basins of attraction of origin"
    let resultsOrigin = measureMultiBasins measurePatternBasin nets originPat
    let resultsNew    = measureMultiBasins measurePatternBasin nets newPat



    let results = zipWith (\a b -> sequence [a, b]) resultsOrigin resultsNew
        printResults d rs = attachLabel $ [pack d] ++ map pack rs

    putStrLn basinHeader
    printMList results [ printResults d | d <- degrees ]