2018-05-15 17:01:38 +00:00
{- # LANGUAGE BangPatterns # -}
2018-05-01 15:09:25 +00:00
module Main where
2018-05-02 21:39:22 +00:00
import Text.Printf
import Control.Monad.Reader
import Numeric.LinearAlgebra
import Data.List
import System.Random
import Control.Concurrent
2018-05-15 17:01:38 +00:00
import Control.Parallel.Strategies
2018-05-02 21:39:22 +00:00
import qualified Debug.Trace as Debug
import System.IO
2018-05-06 22:44:12 +00:00
import ArbitraryEnzymeTree
import Environment
2018-05-02 21:39:22 +00:00
-- Example definitions
-- -------------------
-- Enzymes
pps :: Enzyme -- uses Phosphor from Substrate to produce PP
2018-05-04 22:23:40 +00:00
pps = Enzyme " PPS " [ ( Substrate Phosphor , 1 ) ] ( ( Substrate Phosphor , - 1 ) , ( Produced PP , 1 ) ) Nothing
2018-05-02 21:39:22 +00:00
fpps :: Enzyme -- PP -> FPP
fpps = makeSimpleEnzyme ( Produced PP ) ( Produced FPP )
-- Predator
greenfly :: Predator -- 20% of plants die to greenfly, but the fly is
greenfly = Predator [] 0.2 -- killed by any toxic Component
-- Environment
2018-05-15 17:01:38 +00:00
exampleEnvironment :: Int -> [ Enzyme ] -> [ ( Predator , Probability ) ] -> [ ( Compound , Amount ) ] -> Environment
exampleEnvironment addedC es pred tox =
2018-05-02 21:39:22 +00:00
Environment
{ soil = [ ( Nitrate , 2 )
, ( Phosphor , 3 )
, ( Photosynthesis , 10 )
]
2018-05-15 17:01:38 +00:00
, predators = pred -- [ (greenfly, 0.1) ]
2018-05-02 21:39:22 +00:00
, metabolismIteration = 100
2018-05-06 22:44:12 +00:00
, maxCompound = maxCompoundWithoutGeneric + addedC
2018-05-15 17:01:38 +00:00
, toxicCompounds = tox --[(Produced FPP,0.1)] ++ tox
, possibleEnzymes = es -- [pps,fpps] ++ es
2018-05-02 21:39:22 +00:00
}
-- Plants
examplePlants :: [ Plant ]
examplePlants = ( \ g -> Plant g defaultAbsorption ) <$> genomes
where
enzymes = [ pps , fpps ]
quantity = [ 1 , 2 ] :: [ Quantity ]
activation = [ 0.7 , 0.9 , 1 ]
genomes = do
e <- permutations enzymes
e' <- subsequences e
q <- quantity
a <- activation
return $ ( , , ) <$> e' <*> [ q ] <*> [ a ]
2018-05-04 22:23:40 +00:00
defaultAbsorption = fmap ( limit Phosphor 2
. limit Nitrate 1
. limit Sulfur 0
) <$> asks soil
2018-05-02 21:39:22 +00:00
-- custom absorbtion with helper-function:
limit :: Nutrient -> Amount -> ( Nutrient , Amount ) -> ( Nutrient , Amount )
limit n a ( n' , a' )
| n == n' = ( n , min a a' ) -- if we should limit, then we do ;)
| otherwise = ( n' , a' )
-- Running the simulation
-- ----------------------
loop :: Int -> [ Plant ] -> Environment -> IO ()
2018-05-04 22:23:40 +00:00
loop loopAmount = loop' loopAmount 0
2018-05-02 21:39:22 +00:00
where
loop' :: Int -> Int -> [ Plant ] -> Environment -> IO ()
loop' loopAmount curLoop plants e = unless ( loopAmount == curLoop ) $ do
2018-05-04 22:23:40 +00:00
putStr " \ ESC [2J \ ESC [H "
2018-05-02 21:39:22 +00:00
printEnvironment e
putStrLn " "
putStrLn $ " Generation " ++ show curLoop ++ " of " ++ show loopAmount ++ " : "
2018-05-04 22:23:40 +00:00
newPlants <- flip runReaderT e $ do
2018-05-23 11:07:34 +00:00
! fs <- sequence ( fitness <$> plants )
2018-05-02 21:39:22 +00:00
let fps = zip plants fs -- gives us plants & their fitness in a tuple
sumFitness = sum fs
2018-05-04 22:23:40 +00:00
pe <- asks possibleEnzymes
2018-05-23 11:07:34 +00:00
tc <- asks toxicCompounds
2018-05-15 17:01:38 +00:00
liftIO $ printPopulation tc pe fps
2018-05-23 11:07:34 +00:00
liftIO $ hFlush stdout
2018-05-02 21:39:22 +00:00
-- generate 100 new plants.
2018-05-04 22:23:40 +00:00
sequence . flip fmap [ 1 .. 100 ] $ \ _ -> do
2018-05-02 21:39:22 +00:00
parent' <- liftIO $ randomRIO ( 0 , sumFitness )
let
-- if we only have one parent in our list, take it.
findParent :: Double -> [ ( Plant , Double ) ] -> Plant
findParent _ [ ( last , _ ) ] = last
-- otherwise count down x to find the parent in the list
findParent x ( ( p , f ) : ps )
| x < f = p
| otherwise = findParent ( x - f ) ps
parent = findParent parent' fps
haploMate parent
hFlush stdout
threadDelay $ 100 * 1000 -- sleep 100ms
loop' loopAmount ( curLoop + 1 ) newPlants e
2018-05-01 15:09:25 +00:00
main :: IO ()
2018-05-02 21:39:22 +00:00
main = do
hSetBuffering stdin NoBuffering
hSetBuffering stdout NoBuffering
2018-05-15 17:01:38 +00:00
randomCompounds <- makeHead ( Substrate Photosynthesis ) <$> generateTreeFromList 40 ( toEnum <$> [ ( maxCompoundWithoutGeneric + 1 ) .. ] :: [ Compound ] ) -- generate roughly x compounds
ds <- randoms <$> newStdGen
probs <- randomRs ( 0.2 , 0.7 ) <$> newStdGen
let emptyPlants = replicate 100 emptyPlant
poisonedTree = poisonTree ds randomCompounds
2018-05-23 11:13:20 +00:00
poisonCompounds = foldMap ( \ ( a , b ) -> [ ( b , a ) | a > 0.5 ] ) poisonedTree
2018-05-15 17:01:38 +00:00
predators <- generatePredators 0.5 poisonedTree
let env = exampleEnvironment ( getTreeSize randomCompounds ) ( generateEnzymeFromTree randomCompounds ) ( zip predators probs ) poisonCompounds
2018-05-06 22:44:12 +00:00
printEnvironment env
2018-05-23 11:13:20 +00:00
writeFile " poison.twopi " $ generateDotFromPoisonTree " poison " 0.5 poisonedTree
2018-05-02 21:39:22 +00:00
putStr " \ ESC [?1049h "
2018-05-15 17:01:38 +00:00
loop 200 emptyPlants env
2018-05-02 21:39:22 +00:00
putStrLn " Simulation ended. Press key to exit. "
_ <- getChar
putStr " \ ESC [?1049l "
-- fitness <- runReaderT (sequence $ (\a -> do p <- absorbNutrients a >>= produceCompounds a; (,,) a p <$> deterPredators p) <$> emptyPlants) exampleEnvironment
-- mapM_ (printf "%15.15s, " . show . toEnum @Compound) [0..maxCompoundWithoutGeneric]
-- putStrLn "Fitness"
-- forM_ fitness $ \(p, c, f) -> do
-- mapM_ (printf "%15.2f, ") (toList c)
-- printf "%15.2f" f
-- putStr "\n"
2018-05-15 17:01:38 +00:00
generatePredators :: Double -> EnzymeTree s ( Double , Compound ) -> IO [ Predator ]
generatePredators threshold t = do
ps <- mapM generatePredators' $ getSubTrees t
2018-05-23 11:07:34 +00:00
return $ filter ( ( /= [] ) . irresistance ) $ concat ps -- filter out predators that are resistant to everything because this does not make sense in our model.
2018-05-15 17:01:38 +00:00
where
2018-05-23 11:13:20 +00:00
generatePredators' :: EnzymeTree s ( Double , Compound ) -> IO [ Predator ]
2018-05-15 17:01:38 +00:00
generatePredators' t = do -- not fully resistant to t, but fully resistant to everything in ts
2018-05-23 11:13:20 +00:00
let comps = foldMap ( \ ( a , b ) -> [ ( a , b ) | a > threshold ] ) t
2018-05-15 17:01:38 +00:00
amount <- randomRIO ( 0 , length comps + 1 ) :: IO Int
forM [ 1 .. amount ] $ \ _ -> do
impact <- randomRIO ( 0.2 , 0.7 )
rands <- randoms <$> newStdGen
2018-05-23 11:13:20 +00:00
let unresists = foldMap ( \ ( ( a , b ) , r ) -> [ b | r * 2 < a ] ) $ zip comps rands
2018-05-15 17:01:38 +00:00
return $ Predator unresists impact
2018-05-02 21:39:22 +00:00
printEnvironment :: Environment -> IO ()
printEnvironment ( Environment soil pred metaIter maxComp toxic possEnz ) =
do
putStrLn " Environment: "
putStrLn $ " Soil: " ++ show soil
putStrLn $ " Predators: " ++ show pred
putStrLn $ " PSM Iters: " ++ show metaIter
putStrLn $ " Compounds: " ++ show ( ( toEnum <$> [ 0 .. maxComp ] ) :: [ Compound ] )
putStrLn $ " Toxic: " ++ show toxic
2018-05-23 11:07:34 +00:00
printPopulation :: [ ( Compound , Amount ) ] -> [ Enzyme ] -> [ ( Plant , Double ) ] -> IO ()
2018-05-15 17:01:38 +00:00
printPopulation toxins es ps = do
2018-05-02 21:39:22 +00:00
let padded i str = take i $ str ++ repeat ' '
2018-05-15 17:01:38 +00:00
putStr $ padded 50 " Population: "
2018-05-04 22:23:40 +00:00
forM_ ps $ \ ( _ , f ) -> putStr ( printColor f '█' )
2018-05-02 21:39:22 +00:00
putStrLn colorOff
forM_ es $ \ e -> do
2018-05-23 11:07:34 +00:00
putStr $ case Data . List . find ( \ ( t , _ ) -> ( t == ) . fst . snd . synthesis $ e ) toxins of
Just ( _ , toxicity ) -> " \ ESC [38;5; " ++ show ( 16 + 36 * 5 + 6 * floor ( 5 * ( 1 - toxicity ) ) + 0 ) ++ " m " -- yellow -> red rainbow for tocixity 0 -> 1
++ padded 50 ( show ( enzymeName e ) ) ++ " \ ESC [0m "
Nothing -> padded 50 ( show ( enzymeName e ) )
2018-05-04 22:23:40 +00:00
forM_ ps $ \ ( Plant g _ , _ ) -> do
2018-05-15 17:01:38 +00:00
let curE = sum $ map ( \ ( _ , q , a ) -> fromIntegral q * a )
2018-05-02 21:39:22 +00:00
. filter ( \ ( e' , _ , _ ) -> e == e' )
$ g
plot x
| x > 2 = " O "
| x > 1 = " + "
| x > 0.7 = " ö "
| x > 0.5 = " o "
| x > 0 = " . "
| otherwise = " _ "
putStr ( plot curE )
putStrLn " "
printColor :: Double -> Char -> String
printColor x c
2018-05-04 22:23:40 +00:00
| x * x < 0.5 = " \ ESC [38;5; " ++ show ( 16 + 36 * 5 + 6 * floor ( 5 * 2 * x' ) + 0 ) ++ " m " ++ [ c ] ++ " "
| otherwise = " \ ESC [38;5; " ++ show ( 16 + 36 * floor ( 5 * 2 * ( 1 - x' ) ) + 6 * 5 + 0 ) ++ " m " ++ [ c ] ++ " "
2018-05-02 21:39:22 +00:00
-- 32 bit
-- | x*x < 0.5 = "\ESC[38;2;255;" ++ (show . floor $ 255*2*x') ++ ";0m" ++ [c] ++ ""
-- | otherwise = "\ESC[38;2;" ++ (show . floor $ 255*2*(1-x')) ++ ";255;0m" ++ [c] ++ ""
where x' = x * x
colorOff :: String
colorOff = " \ ESC [0m "
2018-05-06 22:44:12 +00:00
generateEnzymeFromTree :: EnzymeTree s Compound -> [ Enzyme ]
generateEnzymeFromTree t = ( makeSimpleEnzyme c . getElement <$> sts )
++ concatMap generateEnzymeFromTree sts
where
c = getElement t
sts = getSubTrees t