-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMain.hs
82 lines (66 loc) · 2.44 KB
/
Main.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
module Main where
{-
| = Example application: trigonometry cheating
Find the trigonometric expression of cos(x) through sin(x)
using our automatic programming method.
-}
import qualified Data.Vector as V
import Data.List ( foldl' )
import Control.Monad ( foldM )
import Math.Probable.Random -- From `probable` package
( vectorOf
, double
)
import AI.MEP
config = defaultConfig {
-- Functions available to genetically produced programs
c'ops = V.fromList [
('*', (*)),
('+', (+)),
-- Avoid division by zero
('/', \x y -> if y < 1e-6 then 1 else x / y),
('-', (-)),
('s', \x _ -> sin x)
]
-- Chromosome length
, c'length = 50
-- Probability to generate a new variable gene
, p'var = 0.1
-- Probability to generate a new constant gene
, p'const = 0.05
-- Probability to generate a new operator is
-- inferred as 1 - 0.1 - 0.5 = 0.85
}
-- | Absolute value distance between two scalar values
dist :: Double -> Double -> Double
dist x y = abs $ x - y
main :: IO ()
main = do
-- A vector of 50 random numbers between 0 and 1 (including 1)
let datasetSize = 50
xs <- runRandIO (vectorOf datasetSize double)
-- Scale the values to the interval of (-pi, pi]
let xs' = V.map ((2*pi *). subtract 0.5) xs
-- Target function f to approximate
function x = (cos x)^2
-- Pairs (x, f(x))
dataset = map (\x -> (x, function x)) $ V.toList xs'
-- Randomly create a population of chromosomes
pop <- runRandIO $ initialize config
let loss = regressionLoss1 dist dataset
-- Evaluate the initial population
let popEvaluated = evaluatePopulation loss pop
norm = fromIntegral datasetSize
putStrLn $ "Average loss in the initial population " ++ show (avgLoss popEvaluated / norm)
-- Declare how to produce the new generation
let nextGeneration = evolve config loss (mutation3 config) crossover binaryTournament
-- Specify the I/O loop, which logs every 5 generation
runIO pop i = do
newPop <- runRandIO $ foldM (\xg _ -> nextGeneration xg) pop [1..generations]
putStrLn $ "Population " ++ show (i * generations) ++ ": average loss " ++ show (avgLoss newPop / norm)
return newPop
where generations = 5
-- The final population
final <- foldM runIO popEvaluated [1..20]
putStrLn "Interpreted expression:"
putStrLn $ generateCode (best final)