org.jgrapht.alg.clustering.GirvanNewmanClustering Maven / Gradle / Ivy
/*
* (C) Copyright 2021-2023, by Dimitrios Michail and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* See the CONTRIBUTORS.md file distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the
* GNU Lesser General Public License v2.1 or later
* which is available at
* http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
*
* SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
*/
package org.jgrapht.alg.clustering;
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import org.jgrapht.Graph;
import org.jgrapht.alg.connectivity.ConnectivityInspector;
import org.jgrapht.alg.interfaces.ClusteringAlgorithm;
import org.jgrapht.alg.scoring.EdgeBetweennessCentrality;
import org.jgrapht.alg.scoring.EdgeBetweennessCentrality.OverflowStrategy;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.builder.GraphTypeBuilder;
import org.jgrapht.util.SupplierUtil;
/**
* The Girvan-Newman clustering algorithm.
*
*
* The algorithm is described in: Girvan, Michelle, and Mark EJ Newman. "Community structure in
* social and biological networks." Proceedings of the national academy of sciences 99.12 (2002):
* 7821-7826.
*
*
* Running time is $O(m^2 n)$ or $O(m^2n + m n^2 \log n)$ for weighted graphs.
*
* @author Dimitrios Michail
*
* @param the graph vertex type
* @param the graph edge type
*/
public class GirvanNewmanClustering
implements ClusteringAlgorithm
{
private Graph graph;
private int k;
private final Iterable startVertices;
private final OverflowStrategy overflowStrategy;
/**
* Create a new clustering algorithm.
*
* @param graph the graph
* @param k the desired number of clusters
*/
public GirvanNewmanClustering(Graph graph, int k)
{
this(graph, k, OverflowStrategy.THROW_EXCEPTION_ON_OVERFLOW, graph.vertexSet());
}
/**
* Create a new clustering algorithm.
*
* @param graph the graph
* @param k the desired number of clusters
* @param overflowStrategy strategy to use if overflow is detected
* @param startVertices vertices from which to start shortest path computations when computing
* edge centralities. This parameter allows the user to compute edge centrality
* contributions only from a subset of the vertices of the graph. If null the whole graph
* vertex set is used.
*/
public GirvanNewmanClustering(
Graph graph, int k, OverflowStrategy overflowStrategy, Iterable startVertices)
{
this.graph = Objects.requireNonNull(graph);
if (k < 1 || k > graph.vertexSet().size()) {
throw new IllegalArgumentException("Illegal number of clusters");
}
this.k = k;
this.overflowStrategy = overflowStrategy;
if (startVertices == null) {
this.startVertices = graph.vertexSet();
} else {
this.startVertices = startVertices;
}
}
@Override
public Clustering getClustering()
{
// copy graph
Graph graphCopy = GraphTypeBuilder
.forGraphType(graph.getType()).edgeSupplier(SupplierUtil.DEFAULT_EDGE_SUPPLIER)
.vertexSupplier(graph.getVertexSupplier()).buildGraph();
for (V v : graph.iterables().vertices()) {
graphCopy.addVertex(v);
}
for (E e : graph.iterables().edges()) {
V sourceVertex = graph.getEdgeSource(e);
V targetVertex = graph.getEdgeTarget(e);
graphCopy.addEdge(sourceVertex, targetVertex);
}
// main algorithm
while (true) {
List> ccs = new ConnectivityInspector<>(graphCopy).connectedSets();
if (ccs.size() == k) {
return new ClusteringImpl<>(ccs);
}
// compute edge centralities
EdgeBetweennessCentrality bc =
new EdgeBetweennessCentrality<>(graphCopy, overflowStrategy, startVertices);
// find edge with max centrality
DefaultEdge maxEdge = null;
double maxCentrality = 0d;
for (Entry entry : bc.getScores().entrySet()) {
if (Double.compare(entry.getValue(), maxCentrality) > 0 || maxEdge == null) {
maxEdge = entry.getKey();
maxCentrality = entry.getValue();
}
}
// remove edge with max centrality
graphCopy.removeEdge(maxEdge);
}
}
}