2 Module : Gargantext.Core.Viz.Phylo.PhyloTools
3 Description : Module dedicated to all the tools needed for making a Phylo
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
11 {-# LANGUAGE ViewPatterns #-}
13 module Gargantext.Core.Viz.Phylo.PhyloTools where
15 import Control.Lens hiding (Level)
16 import Data.List (sort, concat, null, union, (++), tails, sortOn, nub, init, tail, partition, tails, nubBy, group, notElem)
17 import Data.Map (Map, elems, fromList, unionWith, keys, member, (!), filterWithKey, fromListWith, empty, restrictKeys)
18 import Data.Set (Set, disjoint)
19 import Data.String (String)
20 import Data.Text (Text,unpack)
21 import Data.Vector (Vector, elemIndex)
22 import Debug.Trace (trace)
23 import Gargantext.Core.Viz.Phylo
24 import Gargantext.Prelude
25 import Prelude (floor,read)
27 import qualified Data.List as List
28 import qualified Data.Map as Map
29 import qualified Data.Set as Set
30 import qualified Data.Text as Text
31 import qualified Data.Vector as Vector
37 -- | To print an important message as an IO()
38 printIOMsg :: String -> IO ()
43 <> "-- | " <> msg <> "\n" )
46 -- | To print a comment as an IO()
47 printIOComment :: String -> IO ()
49 putStrLn ( "\n" <> cmt <> "\n" )
56 -- truncate' :: Double -> Int -> Double
57 -- truncate' x n = (fromIntegral (floor (x * t))) / t
60 truncate' :: Double -> Int -> Double
61 truncate' x n = (fromIntegral $ (floor (x * t) :: Int)) / t
67 getInMap :: Int -> Map Int Double -> Double
73 roundToStr :: (PrintfArg a, Floating a) => Int -> a -> String
74 roundToStr = printf "%0.*f"
77 countSup :: Double -> [Double] -> Int
78 countSup s l = length $ filter (>s) l
81 dropByIdx :: Int -> [a] -> [a]
82 dropByIdx k l = take k l ++ drop (k+1) l
85 elemIndex' :: Eq a => a -> [a] -> Int
86 elemIndex' e l = case (List.elemIndex e l) of
87 Nothing -> panic ("[ERR][Viz.Phylo.PhyloTools] element not in list")
91 commonPrefix :: Eq a => [a] -> [a] -> [a] -> [a]
92 commonPrefix lst lst' acc =
93 if (null lst || null lst')
95 else if (head' "commonPrefix" lst == head' "commonPrefix" lst')
96 then commonPrefix (tail lst) (tail lst') (acc ++ [head' "commonPrefix" lst])
100 ---------------------
101 -- | Foundations | --
102 ---------------------
105 -- | Is this Ngrams a Foundations Root ?
106 isRoots :: Ngrams -> Vector Ngrams -> Bool
107 isRoots n ns = Vector.elem n ns
109 -- | To transform a list of nrams into a list of foundation's index
110 ngramsToIdx :: [Ngrams] -> Vector Ngrams -> [Int]
111 ngramsToIdx ns fdt = map (\n -> fromJust $ elemIndex n fdt) ns
113 -- | To transform a list of sources into a list of sources' index
114 sourcesToIdx :: [Text] -> Vector Text -> [Int]
115 sourcesToIdx ss ps = nub $ map (\s -> fromJust $ elemIndex s ps) ss
117 -- | To transform a list of Ngrams Indexes into a Label
118 ngramsToLabel :: Vector Ngrams -> [Int] -> Text
119 ngramsToLabel ngrams l = Text.unwords $ tail' "ngramsToLabel" $ concat $ map (\n -> ["|",n]) $ ngramsToText ngrams l
121 idxToLabel :: [Int] -> String
122 idxToLabel l = List.unwords $ tail' "idxToLabel" $ concat $ map (\n -> ["|",show n]) l
124 idxToLabel' :: [Double] -> String
125 idxToLabel' l = List.unwords $ tail' "idxToLabel" $ concat $ map (\n -> ["|",show n]) l
127 -- | To transform a list of Ngrams Indexes into a list of Text
128 ngramsToText :: Vector Ngrams -> [Int] -> [Text]
129 ngramsToText ngrams l = map (\idx -> ngrams Vector.! idx) l
136 -- | To transform a list of periods into a set of Dates
137 periodsToYears :: [(Date,Date)] -> Set Date
138 periodsToYears periods = (Set.fromList . sort . concat)
139 $ map (\(d,d') -> [d..d']) periods
142 findBounds :: [Date] -> (Date,Date)
144 let dates' = sort dates
145 in (head' "findBounds" dates', last' "findBounds" dates')
148 toPeriods :: [Date] -> Int -> Int -> [(Date,Date)]
149 toPeriods dates p s =
150 let (start,end) = findBounds dates
151 in map (\dates' -> (head' "toPeriods" dates', last' "toPeriods" dates'))
152 $ chunkAlong p s [start .. end]
155 toFstDate :: [Text] -> Text
160 let d' = read (filter (\c -> notElem c ['U','T','C',' ',':','-']) $ unpack d)::Int
163 toLstDate :: [Text] -> Text
169 let d' = read (filter (\c -> notElem c ['U','T','C',' ',':','-']) $ unpack d)::Int
173 getTimeScale :: Phylo -> [Char]
174 getTimeScale p = case (timeUnit $ getConfig p) of
175 Epoch _ _ _ -> "epoch"
177 Month _ _ _ -> "month"
182 -- | Get a regular & ascendante timeScale from a given list of dates
183 toTimeScale :: [Date] -> Int -> [Date]
184 toTimeScale dates step =
185 let (start,end) = findBounds dates
186 in [start, (start + step) .. end]
189 getTimeStep :: TimeUnit -> Int
190 getTimeStep time = case time of
197 getTimePeriod :: TimeUnit -> Int
198 getTimePeriod time = case time of
205 getTimeFrame :: TimeUnit -> Int
206 getTimeFrame time = case time of
218 -- | To find if l' is nested in l
219 isNested :: Eq a => [a] -> [a] -> Bool
222 | length l' > length l = False
223 | (union l l') == l = True
227 -- | To filter Fis with small Support but by keeping non empty Periods
228 keepFilled :: (Int -> [a] -> [a]) -> Int -> [a] -> [a]
229 keepFilled f thr l = if (null $ f thr l) && (not $ null l)
230 then keepFilled f (thr - 1) l
234 traceClique :: Map (Date, Date) [Clustering] -> String
235 traceClique mFis = foldl (\msg cpt -> msg <> show (countSup cpt cliques) <> " (>" <> show (cpt) <> ") " ) "" [1..6]
237 --------------------------------------
239 cliques = sort $ map (fromIntegral . length . _clustering_roots) $ concat $ elems mFis
240 --------------------------------------
243 traceSupport :: Map (Date, Date) [Clustering] -> String
244 traceSupport mFis = foldl (\msg cpt -> msg <> show (countSup cpt supports) <> " (>" <> show (cpt) <> ") " ) "" [1..6]
246 --------------------------------------
248 supports = sort $ map (fromIntegral . _clustering_support) $ concat $ elems mFis
249 --------------------------------------
252 traceFis :: [Char] -> Map (Date, Date) [Clustering] -> Map (Date, Date) [Clustering]
253 traceFis msg mFis = trace ( "\n" <> "-- | " <> msg <> " : " <> show (sum $ map length $ elems mFis) <> "\n"
254 <> "Support : " <> (traceSupport mFis) <> "\n"
255 <> "Nb Ngrams : " <> (traceClique mFis) <> "\n" ) mFis
263 getCliqueSupport :: Cluster -> Int
264 getCliqueSupport unit = case unit of
268 getCliqueSize :: Cluster -> Int
269 getCliqueSize unit = case unit of
278 listToCombi' :: [a] -> [(a,a)]
279 listToCombi' l = [(x,y) | (x:rest) <- tails l, y <- rest]
281 listToEqual' :: Eq a => [a] -> [(a,a)]
282 listToEqual' l = [(x,y) | x <- l, y <- l, x == y]
284 listToKeys :: Eq a => [a] -> [(a,a)]
285 listToKeys lst = (listToCombi' lst) ++ (listToEqual' lst)
287 listToMatrix :: [Int] -> Map (Int,Int) Double
288 listToMatrix lst = fromList $ map (\k -> (k,1)) $ listToKeys $ sort lst
290 listToMatrix' :: [Ngrams] -> Map (Ngrams,Ngrams) Int
291 listToMatrix' lst = fromList $ map (\k -> (k,1)) $ listToKeys $ sort lst
293 listToSeq :: Eq a => [a] -> [(a,a)]
294 listToSeq l = nubBy (\x y -> fst x == fst y) $ [ (x,y) | (x:rest) <- tails l, y <- rest ]
296 sumCooc :: Cooc -> Cooc -> Cooc
297 sumCooc cooc cooc' = unionWith (+) cooc cooc'
299 getTrace :: Cooc -> Double
300 getTrace cooc = sum $ elems $ filterWithKey (\(k,k') _ -> k == k') cooc
302 coocToDiago :: Cooc -> Cooc
303 coocToDiago cooc = filterWithKey (\(k,k') _ -> k == k') cooc
305 -- | To build the local cooc matrix of each phylogroup
306 ngramsToCooc :: [Int] -> [Cooc] -> Cooc
307 ngramsToCooc ngrams coocs =
308 let cooc = foldl (\acc cooc' -> sumCooc acc cooc') empty coocs
309 pairs = listToKeys ngrams
310 in filterWithKey (\k _ -> elem k pairs) cooc
317 getGroupId :: PhyloGroup -> PhyloGroupId
318 getGroupId g = ((g ^. phylo_groupPeriod, g ^. phylo_groupScale), g ^. phylo_groupIndex)
320 idToPrd :: PhyloGroupId -> Period
321 idToPrd id = (fst . fst) id
323 groupByField :: Ord a => (PhyloGroup -> a) -> [PhyloGroup] -> Map a [PhyloGroup]
324 groupByField toField groups = fromListWith (++) $ map (\g -> (toField g, [g])) groups
326 getPeriodPointers :: Filiation -> PhyloGroup -> [Pointer]
327 getPeriodPointers fil g =
329 ToChilds -> g ^. phylo_groupPeriodChilds
330 ToParents -> g ^. phylo_groupPeriodParents
331 ToChildsMemory -> undefined
332 ToParentsMemory -> undefined
334 filterProximity :: Proximity -> Double -> Double -> Bool
335 filterProximity proximity thr local =
337 WeightedLogJaccard _ _ -> local >= thr
338 WeightedLogSim _ _ -> local >= thr
339 Hamming _ _ -> undefined
341 getProximityName :: Proximity -> String
342 getProximityName proximity =
344 WeightedLogJaccard _ _ -> "WLJaccard"
345 WeightedLogSim _ _ -> "WeightedLogSim"
346 Hamming _ _ -> "Hamming"
352 addPointers :: Filiation -> PointerType -> [Pointer] -> PhyloGroup -> PhyloGroup
353 addPointers fil pty pointers g =
355 TemporalPointer -> case fil of
356 ToChilds -> g & phylo_groupPeriodChilds .~ pointers
357 ToParents -> g & phylo_groupPeriodParents .~ pointers
358 ToChildsMemory -> undefined
359 ToParentsMemory -> undefined
360 ScalePointer -> case fil of
361 ToChilds -> g & phylo_groupScaleChilds .~ pointers
362 ToParents -> g & phylo_groupScaleParents .~ pointers
363 ToChildsMemory -> undefined
364 ToParentsMemory -> undefined
366 toPointer' :: Double -> Pointer -> Pointer'
367 toPointer' thr pt = (fst pt,(thr,snd pt))
370 addMemoryPointers :: Filiation -> PointerType -> Double -> [Pointer] -> PhyloGroup -> PhyloGroup
371 addMemoryPointers fil pty thr pointers g =
373 TemporalPointer -> case fil of
374 ToChilds -> undefined
375 ToParents -> undefined
376 ToChildsMemory -> g & phylo_groupPeriodMemoryChilds .~ (concat [(g ^. phylo_groupPeriodMemoryChilds),(map (\pt -> toPointer' thr pt) pointers)])
377 ToParentsMemory -> g & phylo_groupPeriodMemoryParents .~ (concat [(g ^. phylo_groupPeriodMemoryParents),(map (\pt -> toPointer' thr pt) pointers)])
378 ScalePointer -> undefined
381 getPeriodIds :: Phylo -> [(Date,Date)]
382 getPeriodIds phylo = sortOn fst
384 $ phylo ^. phylo_periods
386 getLevelParentId :: PhyloGroup -> PhyloGroupId
387 getLevelParentId g = fst $ head' "getLevelParentId" $ g ^. phylo_groupScaleParents
389 getLastLevel :: Phylo -> Scale
390 getLastLevel phylo = last' "lastLevel" $ getLevels phylo
392 getLevels :: Phylo -> [Scale]
393 getLevels phylo = nub
395 $ keys $ view ( phylo_periods
397 . phylo_periodScales ) phylo
399 getSeaElevation :: Phylo -> SeaElevation
400 getSeaElevation phylo = seaElevation (getConfig phylo)
403 getPhyloSeaRiseStart :: Phylo -> Double
404 getPhyloSeaRiseStart phylo = case (getSeaElevation phylo) of
408 getPhyloSeaRiseSteps :: Phylo -> Double
409 getPhyloSeaRiseSteps phylo = case (getSeaElevation phylo) of
414 getConfig :: Phylo -> PhyloConfig
415 getConfig phylo = (phylo ^. phylo_param) ^. phyloParam_config
418 setConfig :: PhyloConfig -> Phylo -> Phylo
419 setConfig config phylo = phylo
420 & phylo_param .~ (PhyloParam
421 ((phylo ^. phylo_param) ^. phyloParam_version)
422 ((phylo ^. phylo_param) ^. phyloParam_software)
425 -- & phylo_param & phyloParam_config & phyloParam_config .~ config
428 getRoots :: Phylo -> Vector Ngrams
429 getRoots phylo = (phylo ^. phylo_foundations) ^. foundations_roots
431 getSources :: Phylo -> Vector Text
432 getSources phylo = _sources (phylo ^. phylo_sources)
434 phyloToLastBranches :: Phylo -> [[PhyloGroup]]
435 phyloToLastBranches phylo = elems
437 $ map (\g -> (g ^. phylo_groupBranchId, [g]))
438 $ getGroupsFromLevel (last' "byBranches" $ getLevels phylo) phylo
440 getGroupsFromLevel :: Scale -> Phylo -> [PhyloGroup]
441 getGroupsFromLevel lvl phylo =
442 elems $ view ( phylo_periods
446 . filtered (\phyloLvl -> phyloLvl ^. phylo_scaleScale == lvl)
447 . phylo_scaleGroups ) phylo
450 getGroupsFromLevelPeriods :: Scale -> [Period] -> Phylo -> [PhyloGroup]
451 getGroupsFromLevelPeriods lvl periods phylo =
452 elems $ view ( phylo_periods
454 . filtered (\phyloPrd -> elem (phyloPrd ^. phylo_periodPeriod) periods)
457 . filtered (\phyloLvl -> phyloLvl ^. phylo_scaleScale == lvl)
458 . phylo_scaleGroups ) phylo
461 getGroupsFromPeriods :: Scale -> Map Period PhyloPeriod -> [PhyloGroup]
462 getGroupsFromPeriods lvl periods =
463 elems $ view ( traverse
466 . filtered (\phyloLvl -> phyloLvl ^. phylo_scaleScale == lvl)
467 . phylo_scaleGroups ) periods
470 updatePhyloGroups :: Scale -> Map PhyloGroupId PhyloGroup -> Phylo -> Phylo
471 updatePhyloGroups lvl m phylo =
476 . filtered (\phyloLvl -> phyloLvl ^. phylo_scaleScale == lvl)
480 let id = getGroupId g
486 updatePeriods :: Map (Date,Date) (Text,Text) -> Phylo -> Phylo
487 updatePeriods periods' phylo =
488 over (phylo_periods . traverse)
490 let prd' = periods' ! (prd ^. phylo_periodPeriod)
491 lvls = map (\lvl -> lvl & phylo_scalePeriodStr .~ prd') $ prd ^. phylo_periodScales
492 in prd & phylo_periodPeriodStr .~ prd'
493 & phylo_periodScales .~ lvls
496 updateQuality :: Double -> Phylo -> Phylo
497 updateQuality quality phylo = phylo { _phylo_quality = quality }
500 traceToPhylo :: Scale -> Phylo -> Phylo
501 traceToPhylo lvl phylo =
502 trace ("\n" <> "-- | End of phylo making at level " <> show (lvl) <> " with "
503 <> show (length $ getGroupsFromLevel lvl phylo) <> " groups and "
504 <> show (length $ nub $ map _phylo_groupBranchId $ getGroupsFromLevel lvl phylo) <> " branches" <> "\n") phylo
510 mergeBranchIds :: [[Int]] -> [Int]
511 mergeBranchIds ids = (head' "mergeBranchIds" . sort . mostFreq') ids
513 -- | 2) find the most Up Left ids in the hierarchy of similarity
514 -- mostUpLeft :: [[Int]] -> [[Int]]
516 -- let groupIds = (map (\gIds -> (length $ head' "gIds" gIds, head' "gIds" gIds)) . groupBy (\id id' -> length id == length id') . sortOn length) ids'
517 -- inf = (fst . minimum) groupIds
518 -- in map snd $ filter (\gIds -> fst gIds == inf) groupIds
519 -- | 1) find the most frequent ids
520 mostFreq' :: [[Int]] -> [[Int]]
522 let groupIds = (map (\gIds -> (length gIds, head' "gIds" gIds)) . group . sort) ids'
523 sup = (fst . maximum) groupIds
524 in map snd $ filter (\gIds -> fst gIds == sup) groupIds
527 mergeMeta :: [Int] -> [PhyloGroup] -> Map Text [Double]
528 mergeMeta bId groups =
529 let ego = head' "mergeMeta" $ filter (\g -> (snd (g ^. phylo_groupBranchId)) == bId) groups
530 in fromList [("breaks",(ego ^. phylo_groupMeta) ! "breaks"),("seaLevels",(ego ^. phylo_groupMeta) ! "seaLevels")]
533 groupsToBranches' :: Map PhyloGroupId PhyloGroup -> [[PhyloGroup]]
534 groupsToBranches' groups =
535 {- run the related component algorithm -}
536 let egos = map (\g -> [getGroupId g]
537 ++ (map fst $ g ^. phylo_groupPeriodParents)
538 ++ (map fst $ g ^. phylo_groupPeriodChilds)
539 ++ (map fst $ g ^. phylo_groupAncestors)) $ elems groups
540 graph = relatedComponents egos
541 {- update each group's branch id -}
543 let groups' = elems $ restrictKeys groups (Set.fromList ids)
544 bId = mergeBranchIds $ map (\g -> snd $ g ^. phylo_groupBranchId) groups'
545 in map (\g -> g & phylo_groupBranchId %~ (\(lvl,_) -> (lvl,bId))) groups') graph
548 relatedComponents :: Ord a => [[a]] -> [[a]]
549 relatedComponents graph = foldl' (\branches groups ->
551 then branches ++ [groups]
553 let branchPart = partition (\branch -> disjoint (Set.fromList branch) (Set.fromList groups)) branches
554 in (fst branchPart) ++ [nub $ concat $ (snd branchPart) ++ [groups]]) [] graph
557 toRelatedComponents :: [PhyloGroup] -> [((PhyloGroup,PhyloGroup),Double)] -> [[PhyloGroup]]
558 toRelatedComponents nodes edges =
559 let ref = fromList $ map (\g -> (getGroupId g, g)) nodes
560 clusters = relatedComponents $ ((map (\((g,g'),_) -> [getGroupId g, getGroupId g']) edges) ++ (map (\g -> [getGroupId g]) nodes))
561 in map (\cluster -> map (\gId -> ref ! gId) cluster) clusters
564 traceSynchronyEnd :: Phylo -> Phylo
565 traceSynchronyEnd phylo =
566 trace ( "-- | End synchronic clustering at level " <> show (getLastLevel phylo)
567 <> " with " <> show (length $ getGroupsFromLevel (getLastLevel phylo) phylo) <> " groups"
568 <> " and " <> show (length $ nub $ map _phylo_groupBranchId $ getGroupsFromLevel (getLastLevel phylo) phylo) <> " branches"
571 traceSynchronyStart :: Phylo -> Phylo
572 traceSynchronyStart phylo =
573 trace ( "\n" <> "-- | Start synchronic clustering at level " <> show (getLastLevel phylo)
574 <> " with " <> show (length $ getGroupsFromLevel (getLastLevel phylo) phylo) <> " groups"
575 <> " and " <> show (length $ nub $ map _phylo_groupBranchId $ getGroupsFromLevel (getLastLevel phylo) phylo) <> " branches"
583 getSensibility :: Proximity -> Double
584 getSensibility proxi = case proxi of
585 WeightedLogJaccard s _ -> s
586 WeightedLogSim s _ -> s
587 Hamming _ _ -> undefined
589 getMinSharedNgrams :: Proximity -> Int
590 getMinSharedNgrams proxi = case proxi of
591 WeightedLogJaccard _ m -> m
592 WeightedLogSim _ m -> m
593 Hamming _ _ -> undefined
599 intersectInit :: Eq a => [a] -> [a] -> [a] -> [a]
600 intersectInit acc lst lst' =
601 if (null lst) || (null lst')
603 else if (head' "intersectInit" lst) == (head' "intersectInit" lst')
604 then intersectInit (acc ++ [head' "intersectInit" lst]) (tail lst) (tail lst')
607 branchIdsToProximity :: PhyloBranchId -> PhyloBranchId -> Double -> Double -> Double
608 branchIdsToProximity id id' thrInit thrStep = thrInit + thrStep * (fromIntegral $ length $ intersectInit [] (snd id) (snd id'))
610 ngramsInBranches :: [[PhyloGroup]] -> [Int]
611 ngramsInBranches branches = nub $ foldl (\acc g -> acc ++ (g ^. phylo_groupNgrams)) [] $ concat branches
614 traceMatchSuccess :: Double -> Double -> Double -> [[[PhyloGroup]]] -> [[[PhyloGroup]]]
615 traceMatchSuccess thr qua qua' nextBranches =
616 trace ( "\n" <> "-- local branches : " <> (init $ show ((init . init . snd)
617 $ (head' "trace" $ head' "trace" $ head' "trace" nextBranches) ^. phylo_groupBranchId))
618 <> ",(1.." <> show (length nextBranches) <> ")]"
619 <> " | " <> show ((length . concat . concat) nextBranches) <> " groups" <> "\n"
620 <> " - splited with success in " <> show (map length nextBranches) <> " sub-branches" <> "\n"
621 <> " - for the local threshold " <> show (thr) <> " ( quality : " <> show (qua) <> " < " <> show(qua') <> ")\n" ) nextBranches
624 traceMatchFailure :: Double -> Double -> Double -> [[PhyloGroup]] -> [[PhyloGroup]]
625 traceMatchFailure thr qua qua' branches =
626 trace ( "\n" <> "-- local branches : " <> (init $ show ((init . snd) $ (head' "trace" $ head' "trace" branches) ^. phylo_groupBranchId))
627 <> ",(1.." <> show (length branches) <> ")]"
628 <> " | " <> show (length $ concat branches) <> " groups" <> "\n"
629 <> " - split with failure for the local threshold " <> show (thr) <> " ( quality : " <> show (qua) <> " > " <> show(qua') <> ")\n"
633 traceMatchNoSplit :: [[PhyloGroup]] -> [[PhyloGroup]]
634 traceMatchNoSplit branches =
635 trace ( "\n" <> "-- local branches : " <> (init $ show ((init . snd) $ (head' "trace" $ head' "trace" branches) ^. phylo_groupBranchId))
636 <> ",(1.." <> show (length branches) <> ")]"
637 <> " | " <> show (length $ concat branches) <> " groups" <> "\n"
638 <> " - unable to split in smaller branches" <> "\n"
642 traceMatchLimit :: [[PhyloGroup]] -> [[PhyloGroup]]
643 traceMatchLimit branches =
644 trace ( "\n" <> "-- local branches : " <> (init $ show ((init . snd) $ (head' "trace" $ head' "trace" branches) ^. phylo_groupBranchId))
645 <> ",(1.." <> show (length branches) <> ")]"
646 <> " | " <> show (length $ concat branches) <> " groups" <> "\n"
647 <> " - unable to increase the threshold above 1" <> "\n"
651 traceMatchEnd :: [PhyloGroup] -> [PhyloGroup]
652 traceMatchEnd groups =
653 trace ("\n" <> "-- | End temporal matching with " <> show (length $ nub $ map (\g -> g ^. phylo_groupBranchId) groups)
654 <> " branches and " <> show (length groups) <> " groups" <> "\n") groups
657 traceTemporalMatching :: [PhyloGroup] -> [PhyloGroup]
658 traceTemporalMatching groups =
659 trace ( "\n" <> "-- | Start temporal matching for " <> show(length groups) <> " groups" <> "\n") groups
662 traceGroupsProxi :: Map (PhyloGroupId,PhyloGroupId) Double -> Map (PhyloGroupId,PhyloGroupId) Double
664 trace ( "\n" <> "-- | " <> show(Map.size m) <> " computed pairs of groups proximity" <> "\n") m