moo-1.0: examples/ExampleMain.hs
-- | The boring part common to many examples: command line options
-- and pretty-printing the results.
module ExampleMain
( exampleMain
, ExampleDefaults(..)
, exampleDefaults
) where
import Moo.GeneticAlgorithm.Binary
import Moo.GeneticAlgorithm.Continuous
import Moo.GeneticAlgorithm.Multiobjective
import Moo.GeneticAlgorithm.Statistics
import Control.Monad (liftM, when)
import Data.List (intercalate)
import System.Console.GetOpt
import System.Environment (getArgs, getProgName)
import System.Exit (exitSuccess)
import Text.Printf
data Flag = RunGenerations Int
| PrintBest Bool
| PrintStats Bool
| DumpAll Bool
| ShowHelp
deriving (Show, Eq)
data ExampleDefaults = ExampleDefaults
{ numGenerations :: Int
, printBest :: Bool
, printStats :: Bool
, dumpAll :: Bool
} deriving (Show, Eq)
exampleDefaults = ExampleDefaults {
numGenerations = 100
, printBest = True
, printStats = False
, dumpAll = False
}
exampleOptions :: ExampleDefaults -> [OptDescr Flag]
exampleOptions c =
[ Option "gn" ["generations"]
(ReqArg (RunGenerations . read) "N")
("number of generations (default: " ++ show (numGenerations c) ++ ")")
, Option "b" ["best"]
(NoArg $ PrintBest True)
("print the best solution" ++ (isDefault (printBest c)))
, Option "" ["no-best"]
(NoArg $ PrintBest False)
("don't print the best solution" ++ (isDefault (not . printBest $ c)))
, Option "d" ["dump"]
(NoArg $ DumpAll True)
("dump the entire population and its objective values" ++ isDefault (dumpAll c))
, Option "" ["no-dump"]
(NoArg $ DumpAll False)
("don't dump the entire population" ++ isDefault (not . dumpAll $ c))
, Option "s" ["stats"]
(NoArg $ PrintStats True)
("print population statistics" ++ isDefault (printStats c))
, Option "" ["no-stats"]
(NoArg $ PrintStats False)
("don't print population statistics" ++ isDefault (not . printStats $ c))
, Option "h" ["help"]
(NoArg ShowHelp)
"show help"
]
where
isDefault :: Bool -> String
isDefault True = " (default)"
isDefault False = ""
updateDefaults :: ExampleDefaults -> [Flag] -> ExampleDefaults
updateDefaults d (RunGenerations n:opts) = updateDefaults (d { numGenerations = n }) opts
updateDefaults d (PrintBest b:opts) = updateDefaults (d { printBest = b }) opts
-- --stats overrid --dump, and vice versa
updateDefaults d (DumpAll b:opts) =
let ps = printStats d
in flip updateDefaults opts (d { dumpAll = b, printStats = ps && (not b)})
updateDefaults d (PrintStats b:opts) =
let da = dumpAll d
in flip updateDefaults opts (d { printStats = b, dumpAll = da && (not b)})
updateDefaults d [] = d
printHeader conf = do
when (printStats conf) $ putStrLn "# best, median"
when (dumpAll conf) $ putStrLn "# x1, x2, ..., objective1, objective2, ..."
printSnapshot conf sorted = do
when (printBest conf) $
if null sorted
then putStrLn "# no solutions"
else putStrLn $ "# best found: " ++ fmtPt (head sorted)
when (printStats conf) $ do
printHeader conf
let ovs = map takeObjectiveValue sorted
let obest = head ovs
let omedian = median ovs
putStrLn $ fmtXs " " [obest, omedian]
when (dumpAll conf) $ do
printHeader conf
-- print the best solution last;
-- (for scatter-plotting it above the others)
flip mapM_ (reverse sorted) $ \p -> putStrLn $ fmtPtOneline p
putStrLn ""
where
fmtPt :: (Show a, Real a, PrintfArg a) => Phenotype a -> String
fmtPt (xs, v) = (printf "%.3g @ [" v) ++ fmtXs ", " xs ++ "]"
fmtPtOneline :: (Show a, Real a, PrintfArg a) => Phenotype a -> String
fmtPtOneline p = let xs = map (fromRational.toRational) . takeGenome $ p
vs = [takeObjectiveValue p]
in fmtXs " " $ xs ++ vs
fmtXs :: (Show a, Real a, PrintfArg a) => String -> [a] -> String
fmtXs sep xs = intercalate sep $ map (printf "%.3g") xs
-- | Run a genetic algorithm defined by @problemtype@, and @step@.
-- Process command line options to change the number of iterations
-- and logging behaviour.
exampleMain :: (Show a, Real a, PrintfArg a)
=> ExampleDefaults -> ProblemType -> Rand [Genome a] -> StepGA Rand a -> IO ()
exampleMain defaults problemtype initialize step = do
let options = exampleOptions defaults
(opts, args, msgs) <- liftM (getOpt Permute options) getArgs
when (ShowHelp `elem` opts) $ do
progname <- getProgName
let header = "usage: " ++ progname ++ " [options]\n\nOptions:\n"
putStrLn (usageInfo header options)
exitSuccess
let conf = updateDefaults defaults opts
let gens = numGenerations conf
result <- runGA initialize (loop (Generations gens) step)
let sorted = bestFirst problemtype $ result
printSnapshot conf sorted