moo-1.2: Moo/GeneticAlgorithm/Multiobjective/Metrics.hs
{-# LANGUAGE RankNTypes #-}
{- | Performance metrics for multiobjective problems.
-}
module Moo.GeneticAlgorithm.Multiobjective.Metrics where
import Data.List (tails, sortBy)
import Data.Function (on)
import Moo.GeneticAlgorithm.Types
import Moo.GeneticAlgorithm.Multiobjective.Types
import Moo.GeneticAlgorithm.Multiobjective.NSGA2
type Point = [Double]
-- | Calculate the hypervolume indicator using WFG algorithm.
--
-- Reference:
-- While, L., Bradstreet, L., & Barone, L. (2012). A fast way of
-- calculating exact hypervolumes. Evolutionary Computation, IEEE
-- Transactions on, 16(1), 86-95.
--
hypervolume :: forall fn a . ObjectiveFunction fn a
=> MultiObjectiveProblem fn -- ^ multiobjective problem @mop@
-> [Objective] -- ^ reference point (the worst point)
-> [MultiPhenotype a] -- ^ a set of solutions to evaluate
-> Double -- ^ hypervolume
hypervolume mop refPoint solutions =
let ptypes = map fst mop :: [ProblemType]
points = map takeObjectiveValues solutions
in wfgHypervolume_sort 0 ptypes refPoint points
-- | Basic (non-optimized) WFG algorithm to calculate hypervolume.
--
-- Reference: While et al. (2012).
wfgHypervolume :: [ProblemType] -- ^ problem types
-> Point -- ^ reference point (the @worst@ point)
-> [Point] -- ^ a set of points
-> Double
wfgHypervolume ptypes worst pts =
let ptsAndTails = zip pts (drop 1 (tails pts)) :: [(Point, [Point])]
exclusiveHvs = map
(\(pt, rest) -> exclusiveHypervolume ptypes worst pt rest)
ptsAndTails
in sum exclusiveHvs
-- | WFG algorithm to calculate hypervolume with sorting optimization.
wfgHypervolume_sort :: Int -- ^ index of the objective to sort
-> [ProblemType] -- ^ problem types
-> Point -- ^ reference point (the @worst@ point)
-> [Point] -- ^ a set of points
-> Double
wfgHypervolume_sort k ptypes worst pts
| null ptypes || length ptypes <= k || k < 0 =
wfgHypervolume_sort 0 ptypes worst pts -- bad input, sort the first objective
| otherwise =
let ptype = ptypes !! k
pts' = sortBy (flip compare `on` get ptype k) pts
in wfgHypervolume ptypes worst pts'
where
get :: ProblemType -> Int -> [Double] -> Double
get Minimizing k objvals
| length objvals > k = objvals !! k
| otherwise = inf
get Maximizing k objvals
| length objvals > k = objvals !! k
| otherwise = - inf
inf :: Double
inf = 1/0
-- | Construct a limited set (a step of the WFG algorithm).
--
-- @
-- limitSet(S, p) = { limit(x, p) | x \in S }
-- where limit(<s1, ..., sn>, <p1, ..., pn>) = < worse(s1,p1), ..., worse(sn, pn)>.
-- @
limitSet :: [ProblemType] -- ^ problem types
-> Point -- ^ reference point
-> [Point] -- ^ original set
-> [Point] -- ^ limited set
limitSet ptypes refPoint =
map (zipWith3 worst ptypes refPoint)
where
worst :: ProblemType -> Double -> Double -> Double
worst Minimizing x y | x > y = x
| otherwise = y
worst Maximizing x y | x < y = x
| otherwise = y
-- | Construct a non-dominated subset (a step of the WFG algorithm).
nondominatedSet :: [ProblemType] -- ^ problem types
-> [Point] -- ^ original set
-> [Point] -- ^ a non-dominated subset
nondominatedSet ptypes points =
let dominates = domination ptypes
dummySolutions = map (\objvals -> ([], objvals)) points :: [MultiPhenotype Double]
fronts = nondominatedSort dominates dummySolutions :: [[MultiPhenotype Double]]
in case fronts of
(nds:_) -> map takeObjectiveValues nds
_ -> []
-- | Calculate inclusive hypervolume of a point @p@ (the size of the
-- part of the objective space dominated by @p@ alone).
inclusiveHypervolume :: [ProblemType] -- ^ problem types
-> Point -- ^ reference point (the @worst@ point)
-> Point -- ^ a point @p@ to evaluate
-> Double -- ^ inclusive hypervolume
inclusiveHypervolume ptypes worst p =
product $ zipWith3 hyperside ptypes worst p
where
hyperside :: ProblemType -> Double -> Double -> Double
hyperside Minimizing upper x = pos $ upper - x
hyperside Maximizing lower x = pos $ x - lower
-- Positive part: to truncate the hypervolume if an unsuitable
-- reference point is given (not the worst one possible)
pos :: Double -> Double
pos x = 0.5 * (x + abs x)
-- | Calculate exclusive hypervolume of a point @p@ relative to the
-- @underlying@ set (the size of the part of the objective space that
-- is dominated by @p@, but is not dominated by any member of the
-- @underlying@ set).
exclusiveHypervolume :: [ProblemType] -- ^ problem types
-> Point -- ^ reference point (the @worst@ point)
-> Point -- ^ a point @p@ to evaluate
-> [Point] -- ^ an @underlying@ set of points
-> Double -- ^ exclusive hypervolume
exclusiveHypervolume ptypes worst p underlying =
let inclusiveHv = inclusiveHypervolume ptypes worst p
nds = nondominatedSet ptypes $ limitSet ptypes p underlying
underlyingHv = wfgHypervolume ptypes worst nds
in inclusiveHv - underlyingHv