2 Module : Gargantext.Viz.Phylo.Tools
3 Description : Phylomemy Tools to build/manage it
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
13 {-# LANGUAGE NoImplicitPrelude #-}
14 {-# LANGUAGE FlexibleContexts #-}
15 {-# LANGUAGE OverloadedStrings #-}
17 module Gargantext.Viz.Phylo.LinkMaker
20 import Control.Lens hiding (both, Level)
21 import Data.List ((++), nub, sortOn, head, null, tail, splitAt, elem)
22 import Data.Tuple.Extra
23 import Gargantext.Prelude hiding (head)
24 import Gargantext.Viz.Phylo
25 import Gargantext.Viz.Phylo.Tools
26 import Gargantext.Viz.Phylo.Metrics.Proximity
27 import qualified Data.List as List
28 import qualified Data.Maybe as Maybe
31 ------------------------------------------------------------------------
32 -- | Make links from Level to Level
35 -- | To choose a LevelLink strategy based an a given Level
36 shouldLink :: (Level,Level) -> PhyloGroup -> PhyloGroup -> Bool
37 shouldLink (lvl,_lvl) g g'
38 | lvl <= 1 = doesContainsOrd (getGroupNgrams g) (getGroupNgrams g')
39 | lvl > 1 = elem (getGroupId g) (getGroupLevelChildsId g')
40 | otherwise = panic ("[ERR][Viz.Phylo.LinkMaker.shouldLink] Level not defined")
43 -- | To set the LevelLinks between a given PhyloGroup and a list of childs/parents PhyloGroups
44 linkGroupToGroups :: (Level,Level) -> PhyloGroup -> [PhyloGroup] -> PhyloGroup
45 linkGroupToGroups (lvl,lvl') current targets
46 | lvl < lvl' = setLevelParents current
47 | lvl > lvl' = setLevelChilds current
50 --------------------------------------
51 setLevelChilds :: PhyloGroup -> PhyloGroup
52 setLevelChilds = over (phylo_groupLevelChilds) addPointers
53 --------------------------------------
54 setLevelParents :: PhyloGroup -> PhyloGroup
55 setLevelParents = over (phylo_groupLevelParents) addPointers
56 --------------------------------------
57 addPointers :: [Pointer] -> [Pointer]
58 addPointers lp = lp ++ Maybe.mapMaybe (\target ->
59 if shouldLink (lvl,lvl') current target
60 then Just ((getGroupId target),1)
62 --------------------------------------
65 -- | To set the LevelLinks between two lists of PhyloGroups
66 linkGroupsByLevel :: (Level,Level) -> Phylo -> [PhyloGroup] -> [PhyloGroup]
67 linkGroupsByLevel (lvl,lvl') p groups = map (\group ->
68 if getGroupLevel group == lvl
69 then linkGroupToGroups (lvl,lvl') group (getGroupsWithFilters lvl' (getGroupPeriod group) p)
73 -- | To set the LevelLink of all the PhyloGroups of a Phylo
74 setLevelLinks :: (Level,Level) -> Phylo -> Phylo
75 setLevelLinks (lvl,lvl') p = alterPhyloGroups (linkGroupsByLevel (lvl,lvl') p) p
78 ------------------------------------------------------------------------
79 -- | Make links from Period to Period
82 -- | To apply the corresponding proximity function based on a given Proximity
83 applyProximity :: Proximity -> PhyloGroup -> PhyloGroup -> (PhyloGroupId, Double)
84 applyProximity prox g1 g2 = case prox of
85 WeightedLogJaccard (WLJParams _ s) -> ((getGroupId g2),weightedLogJaccard s (getGroupCooc g1) (unifySharedKeys (getGroupCooc g2) (getGroupCooc g1)))
86 Hamming (HammingParams _) -> ((getGroupId g2),hamming (getGroupCooc g1) (unifySharedKeys (getGroupCooc g2) (getGroupCooc g1)))
87 _ -> panic ("[ERR][Viz.Phylo.Example.applyProximity] Proximity function not defined")
90 -- | To get the next or previous PhyloPeriod based on a given PhyloPeriodId
91 getNextPeriods :: Filiation -> PhyloPeriodId -> [PhyloPeriodId] -> [PhyloPeriodId]
92 getNextPeriods to' id l = case to' of
93 Descendant -> unNested id ((tail . snd) next)
94 Ascendant -> unNested id ((reverse . fst) next)
95 _ -> panic ("[ERR][Viz.Phylo.Example.getNextPeriods] Filiation type not defined")
97 --------------------------------------
98 next :: ([PhyloPeriodId], [PhyloPeriodId])
100 --------------------------------------
102 idx = case (List.elemIndex id l) of
103 Nothing -> panic ("[ERR][Viz.Phylo.Example.getNextPeriods] PhyloPeriodId not defined")
105 --------------------------------------
106 -- | To have an non-overlapping next period
107 unNested :: PhyloPeriodId -> [PhyloPeriodId] -> [PhyloPeriodId]
110 | nested (fst $ head l') x = unNested x (tail l')
111 | nested (snd $ head l') x = unNested x (tail l')
113 --------------------------------------
114 nested :: Date -> PhyloPeriodId -> Bool
115 nested d prd = d >= fst prd && d <= snd prd
116 --------------------------------------
119 -- | To find the best set (max = 2) of Childs/Parents candidates based on a given Proximity mesure until a maximum depth (max = Period + 5 units )
120 findBestCandidates :: Filiation -> Int -> Int -> Proximity -> PhyloGroup -> Phylo -> [(PhyloGroupId, Double)]
121 findBestCandidates to' depth max' prox group p
122 | depth > max' || null next = []
123 | (not . null) best = take 2 best
124 | otherwise = findBestCandidates to' (depth + 1) max' prox group p
126 --------------------------------------
127 next :: [PhyloPeriodId]
128 next = getNextPeriods to' (getGroupPeriod group) (getPhyloPeriods p)
129 --------------------------------------
130 candidates :: [PhyloGroup]
131 candidates = getGroupsWithFilters (getGroupLevel group) (head next) p
132 --------------------------------------
133 scores :: [(PhyloGroupId, Double)]
134 scores = map (\group' -> applyProximity prox group group') candidates
135 --------------------------------------
136 best :: [(PhyloGroupId, Double)]
139 $ filter (\(_id,score) -> case prox of
140 WeightedLogJaccard (WLJParams thr _) -> score >= thr
141 Hamming (HammingParams thr) -> score <= thr
142 Filiation -> panic "[ERR][Viz.Phylo.LinkMaker.findBestCandidates] Filiation"
144 --------------------------------------
147 -- | To add a new list of Pointers into an existing Childs/Parents list of Pointers
148 makePair :: Filiation -> PhyloGroup -> [(PhyloGroupId, Double)] -> PhyloGroup
149 makePair to' group ids = case to' of
150 Descendant -> over (phylo_groupPeriodChilds) addPointers group
151 Ascendant -> over (phylo_groupPeriodParents) addPointers group
152 _ -> panic ("[ERR][Viz.Phylo.Example.makePair] Filiation type not defined")
154 --------------------------------------
155 addPointers :: [Pointer] -> [Pointer]
156 addPointers l = nub $ (l ++ ids)
157 --------------------------------------
160 -- | To pair all the Phylogroups of given PhyloLevel to their best Parents or Childs
161 interTempoMatching :: Filiation -> Level -> Proximity -> Phylo -> Phylo
162 interTempoMatching to' lvl prox p = alterPhyloGroups
165 if (getGroupLevel group) == lvl
168 --------------------------------------
169 candidates :: [(PhyloGroupId, Double)]
170 candidates = findBestCandidates to' 1 5 prox group p
171 --------------------------------------
173 makePair to' group candidates