clumpiness-0.17.0.0: src/Math/Clumpiness/Pinpoint.hs
-- Pinpoint
-- By Gregory W. Schwartz
module Math.Clumpiness.Pinpoint where
-- Built-in
import Data.Maybe
import qualified Data.Map as M
import qualified Data.Set as Set
import qualified Data.Sequence as Seq
import Data.Monoid
import Data.Tree
import Control.Arrow
-- Cabal
import Math.TreeFun.Tree
import Math.TreeFun.Types
-- Local
import Math.Clumpiness.Types
import Math.Clumpiness.Utilities
import Math.Clumpiness.Algorithms
-- | Filter the PropertyMap based on the tree, getting rid of extra vertices
getValidPropertyMap :: (Ord a, Ord b)
=> Tree (SuperNode a)
-> PropertyMap a b
-> PropertyMap a b
getValidPropertyMap tree = M.filterWithKey (\k _ -> Set.member k vertices)
where
vertices = Set.fromList . map myRootLabel . flatten $ tree
-- | Convert a subtree first node to a root
rootSubtree :: Tree (SuperNode a) -> Tree (SuperNode a)
rootSubtree ( Node { rootLabel = SuperNode { myRootLabel = x
, myLeaves = y
}
, subForest = xs
}
) =
Node { rootLabel = SuperNode { myRootLabel = x
, myLeaves = y
, myParent = SuperRoot
}
, subForest = xs
}
-- | Check if a node belongs to a label
viableNode :: (Ord a, Ord b) => (b -> Bool) -> PropertyMap a b -> a -> Bool
viableNode viable propertyMap x = (>= Just 0)
. fmap (Seq.length . fmap viable)
. M.lookup x
$ propertyMap
-- | Assign the clumpiness to each subtree and add them to a list. Ignore
-- if the vertex is a root
pinpointRecursion :: (Ord a, Ord b)
=> (b -> Bool)
-> PropertyMap a b
-> Tree (SuperNode a)
-> Seq.Seq (Pinpoint a b)
pinpointRecursion _ _ (Node { subForest = [] }) = Seq.empty
pinpointRecursion viable propertyMap tree@( Node { rootLabel = SuperNode { myRootLabel = label
, myLeaves = descendents
, myParent = parent
}
, subForest = xs }
) =
case newPinpoint of
Nothing -> continue
(Just x) -> x Seq.<| continue
where
continue = mconcat
. map (pinpointRecursion viable validPropertyMap)
$ xs
newPinpoint = case parent of
SuperRoot -> Nothing
_ -> Just
Pinpoint { pinpointLabel = label
, pinpointClumpiness = clump
, pinpointLeaves = relevantLeaves
}
relevantLeaves = Seq.fromList
. filter (viableNode viable validPropertyMap)
. M.keys
$ descendents
clump = Seq.fromList
$ generateClumpMap viable validPropertyMap newTree
validPropertyMap = getValidPropertyMap newTree propertyMap
newTree = filterRootLeaves . rootSubtree $ tree
-- | Return the clumpiness vertices in the tree based on the minimum
-- clumpiness and the minimum number of descendent leaves. Here, viable is
-- the same usage as in Algorithms. The clumpiness values are only reported
-- if they are above a threshold of minClumpiness.
pinpoint :: (Ord a, Ord b) => Double
-> Int
-> (b -> Bool)
-> PropertyMap a b
-> Tree (SuperNode a)
-> Seq.Seq (Pinpoint a b)
pinpoint minClumpiness minLeaves viable propertyMap tree =
Seq.filter
( \x -> (not . Seq.null . pinpointClumpiness $ x)
&& (Seq.length . pinpointLeaves $ x) >= minLeaves
)
. fmap (\x -> x { pinpointClumpiness = filterClumpSeq
. pinpointClumpiness
$ x
}
)
$ pinpointList
where
pinpointList = pinpointRecursion viable propertyMap tree
validPropertyMap subtree = getValidPropertyMap subtree propertyMap
filterClumpSeq = Seq.filter ((>= minClumpiness) . thd')