Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
clustering-louvain
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
gargantext
clustering-louvain
Commits
37e0cd91
Commit
37e0cd91
authored
Mar 20, 2020
by
Przemyslaw Kaminski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[flouvain] more work on the louvain algorithm
parent
35b7c4c1
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
122 additions
and
10 deletions
+122
-10
FLouvain.hs
src/Data/Graph/Clustering/FLouvain.hs
+122
-10
No files found.
src/Data/Graph/Clustering/FLouvain.hs
View file @
37e0cd91
...
...
@@ -39,6 +39,8 @@ module Data.Graph.Clustering.FLouvain
import
Protolude
import
Data.Graph.Inductive
import
qualified
Data.List
as
DL
import
Data.Tuple.Extra
(
fst3
)
-- "glue" : function to gather/merge communities
-- "klue" : function to split communities
...
...
@@ -70,29 +72,52 @@ xdfsFoldWith d f acc (v:vs) g = case match v g of
(
Nothing
,
g'
)
->
xdfsFoldWith
d
f
acc
vs
g'
-- This is the \Sum_in in formula (2) of Louvain paper
type
WeightSum
=
Double
newtype
Community
=
Community
{
unCommunity
::
([
Node
],
WeightSum
)
}
-- Our basic graph. Nodes have custom labels. Edges have weight assigned to them.
type
FEdge
=
Double
type
FGraph
a
=
Gr
a
FEdge
-- | This is the \Sum_in in formula (2) of Louvain paper
type
InWeightSum
=
Double
-- | This is the \Sum_tot in formula (2) of Louvain paper
type
TotWeightSum
=
Double
newtype
Community
=
Community
{
unCommunity
::
([
Node
],
InWeightSum
,
TotWeightSum
)
}
type
CGrNode
=
Node
type
CGrEdge
=
Double
type
CGrEdge
=
(
InWeightSum
,
TotWeightSum
)
type
CGr
=
Gr
Community
CGrEdge
-- ALGORITHM
type
Delta
a
b
=
Gr
a
b
->
Node
->
Community
->
Double
-- | Delta Q function from Louvain paper (2).
delta
::
Delta
a
b
delta
gr
n
(
Community
(
ns
,
inWeightSum
,
totWeightSum
))
=
0.0
-- | One iteration step takes the graph and existing communities as a graph and
-- computes new community graph
iteration
::
(
Graph
gr
)
=>
gr
a
b
->
CGr
->
CGr
iteration
::
FGraph
a
->
CGr
->
CGr
iteration
gr
cs
=
xdfsFoldWith
suc'
step
cs
(
nodes
gr
)
gr
-- TODO Remember to filter out empty Communities
step
::
CFunFold
a
b
CGr
-- | Step for one node. We try re-assign it to a neighbouring community, where
-- the increase of modularity for graph will be the largest
step
::
CFunFold
a
FEdge
CGr
step
(
p
,
v
,
l
,
s
)
cgr
=
cgr
where
n
c
=
nodeCommunity
v
cgr
mN
c
=
nodeCommunity
v
cgr
ncs
=
nodeNeighbours
v
cgr
-- We move node from community nc into ncs
moves
::
Maybe
(
LNode
Community
,
[
LNode
Community
])
moves
=
case
mNc
of
Nothing
->
Nothing
Just
nc
->
Just
(
makeMove
OutOf
nc
,
map
(
makeMove
Into
)
ncs
)
makeMove
::
Direction
->
LNode
Community
->
LNode
Community
makeMove
direction
(
cn
,
c
)
=
(
cn
,
moveNodeWithNeighbours
(
p
<>
s
)
v
direction
c
)
-- TODO Compute \Delta Q (gain of moving node v into Community C) which consists of:
-- - Community WeightSum
-- - sum of weights of links incident to nodes in C
...
...
@@ -105,17 +130,104 @@ step (p, v, l, s) cgr = cgr
-- COMMUNITY GRAPH FUNCTIONS
-- | 'Direction' when moving node 'Into'/'OutOf' community
data
Direction
=
Into
|
OutOf
-- | Given 'Node' and 'Community' graph, find the 'LNode' of 'Community' which
-- contains the node
nodeCommunity
::
Node
->
CGr
->
Maybe
(
LNode
Community
)
nodeCommunity
n
cgr
=
head
(
filter
f
$
labNodes
cgr
)
where
f
::
(
a
,
Community
)
->
Bool
f
(
_
,
Community
ns
)
=
n
`
elem
`
fst
ns
f
(
_
,
Community
com
)
=
n
`
elem
`
fst3
com
-- | Find 'LNode
s'
of 'Community' graph neighbouring a given node
-- | Find 'LNode
's
of 'Community' graph neighbouring a given node
nodeNeighbours
::
Node
->
CGr
->
[
LNode
Community
]
nodeNeighbours
n
cgr
=
case
nodeCommunity
n
cgr
of
Nothing
->
[]
Just
c
@
(
cn
,
_
)
->
[
c
]
<>
mapMaybe
(
lnode
cgr
)
(
neighbors
cgr
cn
)
Just
(
cn
,
_
)
->
mapMaybe
(
lnode
cgr
)
(
neighbors
cgr
cn
)
-- | Find 'Ajd CGrEdge's of 'Community' graph neighbouring a given node
nodeLNeighbours
::
Node
->
CGr
->
Adj
CGrEdge
nodeLNeighbours
n
cgr
=
case
nodeCommunity
n
cgr
of
Nothing
->
[]
Just
(
cn
,
_
)
->
lneighbors
cgr
cn
-- | Moves 'Node' in the 'Direction' of 'Community' and recomputes 'Community''s weights
moveNode
::
FGraph
a
->
Node
->
Direction
->
Community
->
Community
moveNode
gr
n
direction
c
=
moveNodeWithNeighbours
lnNeighbors
n
direction
c
where
lnNeighbors
::
Adj
FEdge
lnNeighbors
=
lneighbors
gr
n
-- | Same asa 'moveNode' above but with only node neighbours, not whole graph
moveNodeWithNeighbours
::
Adj
FEdge
->
Node
->
Direction
->
Community
->
Community
moveNodeWithNeighbours
lnNeighbors
n
direction
(
Community
(
ns
,
inwsum
,
totwsum
))
=
Community
(
newNs
,
newInWsum
,
newTotWsum
)
where
newNs
=
case
direction
of
Into
->
n
:
ns
OutOf
->
DL
.
delete
n
ns
comNeighbors
::
Adj
FEdge
comNeighbors
=
filter
(
\
ln
->
snd
ln
`
elem
`
ns
)
lnNeighbors
nonComNeighbors
::
Adj
FEdge
nonComNeighbors
=
filter
(
\
ln
->
snd
ln
`
notElem
`
ns
)
lnNeighbors
-- Update InWeightSum with connections between node and the community
sumN
::
InWeightSum
sumN
=
sum
$
map
fst
comNeighbors
-- Update TotWeightSum, subtracting connections between node and community
-- and adding connections of node to non-community
sumNonCom
::
TotWeightSum
sumNonCom
=
sum
$
map
fst
nonComNeighbors
directionN
=
case
direction
of
Into
->
1
OutOf
->
-
1
newInWsum
=
inwsum
+
directionN
*
sumN
newTotWsum
=
totwsum
+
directionN
*
(
sumN
-
sumNonCom
)
{-
-- | Moves 'Node' between two 'Community'ies, recomputing their weights
moveNode' :: FGraph a -> Node -> (Community, Community) -> (Community, Community)
moveNode' gr n cFromTo = moveNodeWithNeighbours' lnNeighbors n cFromTo
where
lnNeighbors :: Adj FEdge
lnNeighbors = lneighbors gr n
-- | Same as 'moveNode' but with direct neighbours list
moveNodeWithNeighbours' :: Adj FEdge -> Node -> (Community, Community) -> (Community, Community)
moveNodeWithNeighbours' lnNeighbors n ((Community (fromNs, fromInWSum, fromTotWSum)), (Community (toNs, toInWSum, toTotWSum))) =
(newFrom, newTo)
where
newFrom = Community (DL.delete n fromNs, newFromInWSum, newFromTotWSum)
newTo = Community (n:toNs, newToInWSum, newToTotWSum)
-- Node is removed, so we reduce the internal weight sum
newFromInWSum = fromInWSum - sumFromN
newFromTotWSum = fromTotWSum + sumFromN - sumFromNonN
-- Node is added, so we increase the internal weight sum
newToInWSum = toInWSum + sumToN
newToTotWSum = toTotWSum + sumToN - sumToNonN
sumFromN :: InWeightSum
sumFromN = sum $ map fst fromComNeighbors
sumFromNonN :: TotWeightSum
sumFromNonN = sum $ map fst fromNonComNeighbors
sumToN :: InWeightSum
sumToN = sum $ map fst toComNeighbors
sumToNonN :: TotWeightSum
sumToNonN = sum $ map fst toNonComNeighbors
fromComNeighbors :: Adj FEdge
fromComNeighbors = filter (\ln -> snd ln `elem` fromNs) lnNeighbors
fromNonComNeighbors :: Adj FEdge
fromNonComNeighbors = filter (\ln -> snd ln `notElem` fromNs) lnNeighbors
toComNeighbors :: Adj FEdge
toComNeighbors = filter (\ln -> snd ln `elem` toNs) lnNeighbors
toNonComNeighbors :: Adj FEdge
toNonComNeighbors = filter (\ln -> snd ln `notElem` toNs) lnNeighbors
-}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment