Friday, January 21, 2011

Evolving a computer with TECS and Genprog

I've started reading The Elements of Computer Systems since it was recommended on Reddit and it's a great read. After the section on boolean gates I decided to have a quick go at build the gates in Haskell. Just implement Nand with pattern matching, and implement all the other gates just in term of functions calls to previously defined gates. The book mentioned "think positive" about implementing Not, so I just T as the second parameter, did't think more about it, even though I wasn't sure having a constant value was OK.

import Data.Generics(Data,Typeable)
import Prelude(Show,Read,Eq)
data Bit=T | F
deriving (Show,Read,Eq,Data,Typeable)
nand :: (Bit, Bit) -> Bit
nand (T,T)=F
nand _=T
not :: Bit -> Bit
not a=nand(a,T)
view raw Logic.hs hosted with ❤ by GitHub


Then I moved on to the other gates. It was interesting to think about boolean logic without Or, for example, after years of only reasoning in terms of And Or and Not. But at some stage I thought "why am I doing this by hand?". I remembered a thread about a new genetic programming library on an Haskell group. Surely generating the gates would be simple enough?
So I got genprog from hackage, and adapted the example for my needs. I started with a very simple system to determine the implementation of Not:

{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses #-}
module Gen where
import GenProg
import Data.Generics
import Control.Monad
import Control.Monad.Random
import Logic
data E=Nand E E
| Const Bit
| Param
deriving (Show,Eq,Data,Typeable)
eval :: E -> Bit -> Bit
eval (Nand e1 e2) p=nand (eval e1 p,eval e2 p)
eval (Const b) _=b
eval Param p=p
notFitness :: E -> Double
notFitness e=let
resF=eval e F
resT=eval e T
in if resF==T && resT==F
then ((realToFrac $ nodes e)*100)
else 100000000
instance GenProg (Rand StdGen) E where
terminal = do
r<-getRandomR (0,2)
return $ [Const T,Const F,Param] !! r
nonterminal = liftM2 Nand terminal terminal
run=let
params = defaultEvolParams { fitness = notFitness }
g = mkStdGen 0
in cachedBest $ evalRand (evolve params) g
view raw Gen.hs hosted with ❤ by GitHub


And of course, the result came quickly enough: not a=nand(a,a). No need for a Const at all! Evolution has beaten me! (OK, I'm only a not-so-intelligent designer, I suppose...)
Now, I'm going to see if this approach can take me all the way to the arithmetic unit...