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 Debug.Trace (trace)
23 import Data.Map (Map, fromListWith, lookup, toList, mapWithKey, elems)
24 import Data.Maybe (catMaybes)
25 import Gargantext.Prelude
26 import Graph.Types (ClusterNode(..))
27 import Data.Ord (Down(..))
28 import Data.IntMap (IntMap)
29 import qualified Data.IntMap as IntMap
30 import qualified Data.List as List
31 import qualified Data.Map as Map
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
52 bridgeness3 :: Confluence
53 -> Map (NodeId, NodeId) Double
54 -> Map (NodeId, NodeId) Double
55 bridgeness3 c m = trace ("bridgeness c' size: " <> (show $ List.length c'))
57 $ map (\(ks, (v1,_v2)) -> (ks,v1))
59 -- $ List.sortOn (Down . (snd . snd))
63 !c' = Map.intersectionWithKey (\_k v1 v2 -> (v1, v2)) m c
67 !n = trace ("bridgeness m size: " <> (show $ List.length m'))
69 $ (fromIntegral $ List.length m') / (10 :: Double)
72 map2intMap :: Map (Int, Int) a -> IntMap (IntMap a)
73 map2intMap m = IntMap.fromListWith (<>)
74 $ map (\((k1,k2), v) -> if k1 < k2
75 then (k1, IntMap.singleton k2 v)
76 else (k2, IntMap.singleton k1 v)
80 look :: (Int,Int) -> IntMap (IntMap a) -> Maybe a
81 look (k1,k2) m = if k1 < k2
82 then case (IntMap.lookup k1 m) of
83 Just m' -> IntMap.lookup k2 m'
90 n = Set.size $ Set.fromList $ as <> bs
92 (as, bs) = List.unzip $ Map.keys m
95 bridgeness :: ToComId a
98 -> Map (NodeId, NodeId) Double
99 -> Map (NodeId, NodeId) Double
100 bridgeness = bridgenessWith nodeId2comId
102 bridgenessWith :: (a -> (Int, Int))
105 -> Map (Int, Int) Double
106 -> Map (Int, Int) Double
107 bridgenessWith f b ns = Map.fromList
111 . groupEdges (Map.fromList $ map f ns)
114 groupEdges :: (Ord a, Ord b1)
117 -> Map (a, a) [((b1, b1), b2)]
118 groupEdges m = fromListWith (<>)
122 n1n2_m = (,) <$> lookup n1 m <*> lookup n2 m
123 n1n2_d = Just [((n1,n2),d)]
124 in (,) <$> n1n2_m <*> n1n2_d
128 -- | TODO : sortOn Confluence
129 filterComs :: (Ord n1, Eq n2)
131 -> Map (n2, n2) [(a3, n1)]
132 -> Map (n2, n2) [(a3, n1)]
133 filterComs _b m = Map.filter (\n -> length n > 0) $ mapWithKey filter' m
138 | otherwise = take 1 $ List.sortOn (Down . snd) a
141 _n = round $ 100 * a' / t
142 a'= fromIntegral $ length a
144 t = fromIntegral $ length $ List.concat $ elems m
146 --------------------------------------------------------------
149 Compute the median of a list
150 From: https://hackage.haskell.org/package/dsp-0.2.5.1/docs/src/Numeric.Statistics.Median.html
151 Compute the center of the list in a more lazy manner
152 and thus halves memory requirement.
154 median :: (Ord a, Fractional a) => [a] -> a
155 median [] = panic "medianFast: empty list has no median"
157 let recurse (x0:_) (_:[]) = x0
158 recurse (x0:x1:_) (_:_:[]) = (x0+x1)/2
159 recurse (_:xs) (_:_:ys) = recurse xs ys
161 panic "median: this error cannot occur in the way 'recurse' is called"