From 3047e46f3478e20250556de833c1b6dd1b1a31f0 Mon Sep 17 00:00:00 2001
From: sanine <sanine.not@pm.me>
Date: Wed, 29 Nov 2023 15:45:44 -0600
Subject: imlement mutateGenomeRemoveNeuron

---
 src/Genome.hs      | 89 +++++++++++++++++++++++++++++++++++++++++++++---------
 src/Mind.hs        |  2 +-
 test/GenomeTest.hs | 50 ++++++++++++++++++++++++++++++
 3 files changed, 126 insertions(+), 15 deletions(-)

diff --git a/src/Genome.hs b/src/Genome.hs
index c3fc361..500512c 100644
--- a/src/Genome.hs
+++ b/src/Genome.hs
@@ -7,11 +7,13 @@ module Genome
   , mutateGene
 
   , mutateGenomeAddInternal
+  , mutateGenomeRemoveInternal
   ) where
 
 
 import Mind (NeuronIndex (..))
 import System.Random
+import Data.Ix
 
 
 data Gene = Gene { source :: NeuronIndex, sink :: NeuronIndex, weight :: Float } deriving (Eq, Show)
@@ -21,8 +23,8 @@ data Genome = Genome { numInput :: Int, numInternal :: Int, numOutput :: Int, ge
 -- choose a random list element
 randomChoice :: RandomGen a => [b] -> a -> (b, a)
 randomChoice xs r =
-  let (index, r') = randomR (0, length xs - 1) r
-  in (xs !! index, r')
+  let (idx, r') = randomR (0, length xs - 1) r
+  in (xs !! idx, r')
 
 
 -- pick a new random source for the gene
@@ -31,12 +33,12 @@ mutateGeneSource genome g r =
   let 
     nInput = numInput genome
     nInternal = numInternal genome
-    index :: Int
-    (index, r') = randomR (0, nInput+nInternal-1) r
+    idx :: Int
+    (idx, r') = randomR (0, nInput+nInternal-1) r
     source' = 
-      if index < nInput
-        then Input index
-        else Internal $ index - nInput
+      if idx < nInput
+        then Input idx
+        else Internal $ idx - nInput
   in ( g { source = source' }, r')
 
 
@@ -46,11 +48,11 @@ mutateGeneSink genome g r =
   let
     nInternal = numInternal genome
     nOutput = numOutput genome
-    index :: Int
-    (index, r') = randomR (0, nInternal+nOutput-1) r
-    sink' = if index < nInternal
-      then Internal index
-      else Output $ index - nInternal
+    idx :: Int
+    (idx, r') = randomR (0, nInternal+nOutput-1) r
+    sink' = if idx < nInternal
+      then Internal idx
+      else Output $ idx - nInternal
   in ( g { sink = sink' }, r' )
 
 
@@ -61,7 +63,7 @@ mutateGeneWeight _ g r =
   let
     w = 2 * (abs $ weight g)
     (weight', r') = randomR (negate w, w) r
-  in ( g { weight = weight' }, r' )
+ in ( g { weight = weight' }, r' )
 
 
 -- randomly mutate gene (modify source, sink, or weight
@@ -77,4 +79,63 @@ mutateGenomeAddInternal genome r = (genome { numInternal = 1 + numInternal genom
 
 -- remove an internal neuron, decrementing sources and incrementing sinks
 mutateGenomeRemoveInternal :: RandomGen a => Genome -> a -> (Genome, a)
-mutateGenomeRemoveInternal = undefined
+mutateGenomeRemoveInternal g r =
+  let
+    (idx, r') = randomR (0, numInternal g - 1) r
+    remove = Internal idx 
+    genes' = map
+      (\(Gene so si w) ->
+        let
+          so' = if (isInternal so) && (so >= remove)
+            then decrementNeuron g so else so
+          si' = if (si < remove) || (not $ isInternal si) then si
+            else if si == remove
+              then
+                let si'' = incrementNeuron g si
+                in if isInternal si'' then si else si''
+              else decrementNeuron g si 
+        in Gene {source=so', sink=si', weight=w}
+      )
+      (genes g)
+  in
+    (g { numInternal = (numInternal g - 1), genes = genes' }, r')
+
+validn :: Genome -> NeuronIndex -> NeuronIndex
+validn g (Input x)
+  | (inRange (0, numInput g - 1) x) = Input x
+  | otherwise = error "out of range Input!"
+validn g (Internal x)
+  | (inRange (0, numInternal g - 1) x) = Internal x
+  | otherwise = error "out of range Internal!"
+validn g (Output x)
+  | (inRange (0, numOutput g - 1) x) = Output x
+  | otherwise = error "out of range Output!"
+
+
+
+incrementNeuron :: Genome -> NeuronIndex -> NeuronIndex
+incrementNeuron g (Input x)
+  | (x >= numInput g - 1) = validn g (Internal 0)
+  | otherwise = validn g (Input $ x+1)
+incrementNeuron g (Internal x)
+  | (x >= numInternal g - 1) = validn g (Output 0)
+  | otherwise = validn g (Internal $ x+1)
+incrementNeuron g (Output x)
+  | (x >= numOutput g - 1) = error "cannot increment past the end of outputs!"
+  | otherwise = validn g (Output $ x+1)
+
+
+decrementNeuron :: Genome -> NeuronIndex -> NeuronIndex
+decrementNeuron g (Input x)
+  | (x <= 0) = error "cannot decrement past the first Input!"
+  | otherwise = validn g (Input $ x-1)
+decrementNeuron g (Internal x)
+  | (x <= 0) = validn g (Input $ numInput g - 1)
+  | otherwise = validn g (Internal $ x-1)
+decrementNeuron g (Output x)
+  | (x <= 0) = validn g (Internal $ numInternal g - 1)
+  | otherwise = validn g (Output $ x-1)
+
+isInternal :: NeuronIndex -> Bool
+isInternal (Internal _) = True
+isInternal _ = False
diff --git a/src/Mind.hs b/src/Mind.hs
index d686e24..010d83b 100644
--- a/src/Mind.hs
+++ b/src/Mind.hs
@@ -14,7 +14,7 @@ import Data.Ix
 import Data.Maybe
 
 -- index different neuron types
-data NeuronIndex = Input Int | Internal Int | Output Int deriving (Show, Eq)
+data NeuronIndex = Input Int | Internal Int | Output Int deriving (Show, Eq, Ord)
 
 getNeuronIndex :: NeuronIndex -> Int
 getNeuronIndex (Input i)    = i
diff --git a/test/GenomeTest.hs b/test/GenomeTest.hs
index 56499a1..8448133 100644
--- a/test/GenomeTest.hs
+++ b/test/GenomeTest.hs
@@ -95,4 +95,54 @@ mutationTests = testGroup "mutations" $
       (numInternal genome') @?= (1 + numInternal genome)
       (numOutput genome') @?= (numOutput genome)
       (genes genome') @?= (genes genome)
+  , testCase "remove internal neuron" $
+    let 
+      r = mkStdGen 1 -- randomR (0, 2) produces (1, 2, 0, ...)
+      genome = Genome 
+        { numInput = 1
+        , numInternal = 3
+        , numOutput = 1
+        , genes =
+          [ Gene { source = M.Input 0, sink = M.Internal 0, weight = 1.0 }
+          , Gene { source = M.Internal 0, sink = M.Internal 1, weight = 1.0 }
+          , Gene { source = M.Internal 1, sink = M.Internal 2, weight = 1.0 }
+          , Gene { source = M.Internal 2, sink = M.Output 0, weight = 1.0 }
+          ]
+        }
+      (genome', r') = mutateGenomeRemoveInternal genome r
+      (genome'', r'') = mutateGenomeRemoveInternal genome r'
+      (genome''', _) = mutateGenomeRemoveInternal genome r''
+    in do
+      (numInput genome') @?= (numInput genome)
+      (numInput genome'') @?= (numInput genome)
+      (numInput genome''') @?= (numInput genome)
+
+      (numInternal genome') @?= (numInternal genome - 1)
+      (numInternal genome'') @?= (numInternal genome - 1)
+      (numInternal genome''') @?= (numInternal genome - 1)
+
+      (numOutput genome') @?= (numOutput genome)
+      (numOutput genome'') @?= (numOutput genome)
+      (numOutput genome''') @?= (numOutput genome)
+
+      (genes genome') @?=
+        [ Gene { source = M.Input 0, sink = M.Internal 0, weight = 1.0 }
+        , Gene { source = M.Internal 0, sink = M.Internal 1, weight = 1.0 }
+        , Gene { source = M.Internal 0, sink = M.Internal 1, weight = 1.0 }
+        , Gene { source = M.Internal 1, sink = M.Output 0, weight = 1.0 }
+        ]
+
+      (genes genome'') @?=
+        [ Gene { source = M.Input 0, sink = M.Internal 0, weight = 1.0 }
+        , Gene { source = M.Internal 0, sink = M.Internal 1, weight = 1.0 }
+        , Gene { source = M.Internal 1, sink = M.Output 0, weight = 1.0 }
+        , Gene { source = M.Internal 1, sink = M.Output 0, weight = 1.0 }
+        ]
+
+      (genes genome''') @?=
+        [ Gene { source = M.Input 0, sink = M.Internal 0, weight = 1.0 }
+        , Gene { source = M.Input 0, sink = M.Internal 0, weight = 1.0 }
+        , Gene { source = M.Internal 0, sink = M.Internal 1, weight = 1.0 }
+        , Gene { source = M.Internal 1, sink = M.Output 0, weight = 1.0 }
+        ]
   ]
-- 
cgit v1.2.1