2 Module : Gargantext.Core.Viz.Graph.Bridgeness
3 Description : Bridgeness filter
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
10 Let be a graph with partitions (from Louvain algo), Bridgeness uniformly
11 filters inter-communities links.
13 TODO rewrite Bridgeness with "equivalence structurale" metrics (Confluence)
14 TODO use Map LouvainNodeId (Map LouvainNodeId)
17 {-# LANGUAGE BangPatterns #-}
19 module Gargantext.Core.Viz.Graph.Bridgeness -- (bridgeness)
22 import Data.List (concat, sortOn)
23 import Data.Map (Map, fromListWith, lookup, toList, mapWithKey, elems)
24 import Data.Maybe (catMaybes, fromMaybe)
26 import Gargantext.Prelude
27 import Graph.Types (ClusterNode(..))
28 import Data.Ord (Down(..))
29 import qualified Data.List as List
30 import qualified Data.Map as Map
31 import qualified Data.Set as Set
33 ----------------------------------------------------------------------
34 type Partitions a = Map (Int, Int) Double -> IO [a]
35 ----------------------------------------------------------------------
37 nodeId2comId :: a -> (NodeId,CommunityId)
40 type CommunityId = Int
42 ----------------------------------------------------------------------
43 instance ToComId ClusterNode where
44 nodeId2comId (ClusterNode i1 i2) = (i1, i2)
46 ----------------------------------------------------------------------
47 ----------------------------------------------------------------------
48 type Bridgeness = Double
49 type Confluence = Map (NodeId, NodeId) Double
51 bridgeness3 :: Confluence
52 -> Map (NodeId, NodeId) Double
53 -> Map (NodeId, NodeId) Double
57 bridgeness2 :: Confluence
58 -> Map (NodeId, NodeId) Double
59 -> Map (NodeId, NodeId) Double
60 bridgeness2 c m = Map.fromList
61 $ List.filter (\((k1,k2),_v) -> if k1 > k2
62 then fromMaybe False (Set.member k2 <$> Map.lookup k1 toKeep)
63 else fromMaybe False (Set.member k1 <$> Map.lookup k2 toKeep)
67 toKeep :: Map NodeId (Set NodeId)
68 !toKeep = Map.fromListWith (<>)
69 $ map (\((k1,k2), _v) -> if k1 > k2
70 then (k1, Set.singleton k2)
71 else (k2, Set.singleton k1)
74 $ List.sortOn (Down . snd)
79 !n = round $ (fromIntegral $ List.length m') / (2 :: Double)
83 n = Set.size $ Set.fromList $ as <> bs
85 (as, bs) = List.unzip $ Map.keys m
88 bridgeness :: ToComId a
91 -> Map (NodeId, NodeId) Double
92 -> Map (NodeId, NodeId) Double
93 bridgeness = bridgenessWith nodeId2comId
95 bridgenessWith :: (a -> (Int, Int))
98 -> Map (Int, Int) Double
99 -> Map (Int, Int) Double
100 bridgenessWith f b ns = Map.fromList
104 . groupEdges (Map.fromList $ map f ns)
107 groupEdges :: (Ord a, Ord b1)
110 -> Map (a, a) [((b1, b1), b2)]
111 groupEdges m = fromListWith (<>)
115 n1n2_m = (,) <$> lookup n1 m <*> lookup n2 m
116 n1n2_d = Just [((n1,n2),d)]
117 in (,) <$> n1n2_m <*> n1n2_d
121 -- | TODO : sortOn Confluence
122 filterComs :: (Ord n1, Eq n2)
124 -> Map (n2, n2) [(a3, n1)]
125 -> Map (n2, n2) [(a3, n1)]
126 filterComs _b m = Map.filter (\n -> length n > 0) $ mapWithKey filter' m
131 | otherwise = take 1 $ sortOn (Down . snd) a
134 _n = round $ 100 * a' / t
135 a'= fromIntegral $ length a
137 t = fromIntegral $ length $ concat $ elems m
139 --------------------------------------------------------------
142 Compute the median of a list
143 From: https://hackage.haskell.org/package/dsp-0.2.5.1/docs/src/Numeric.Statistics.Median.html
144 Compute the center of the list in a more lazy manner
145 and thus halves memory requirement.
147 median :: (Ord a, Fractional a) => [a] -> a
148 median [] = panic "medianFast: empty list has no median"
150 let recurse (x0:_) (_:[]) = x0
151 recurse (x0:x1:_) (_:_:[]) = (x0+x1)/2
152 recurse (_:xs) (_:_:ys) = recurse xs ys
154 panic "median: this error cannot occur in the way 'recurse' is called"