org.jgrapht.alg.CliqueMinimalSeparatorDecomposition Maven / Gradle / Ivy
/* ==========================================
* JGraphT : a free Java graph-theory library
* ==========================================
*
* Project Info: http://jgrapht.sourceforge.net/
* Project Creator: Barak Naveh (http://sourceforge.net/users/barak_naveh)
*
* (C) Copyright 2003-2015, by Barak Naveh and Contributors.
*
* This program and the accompanying materials are dual-licensed under
* either
*
* (a) the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation, or (at your option) any
* later version.
*
* or (per the licensee's choosing)
*
* (b) the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation.
*/
/* -----------------
* CliqueMinimalSeparatorDecomposition.java
* -----------------
* (C) Copyright 2015, by Florian Buenzli and Contributors.
*
* Original Author: Florian Buenzli
* Contributor(s): Thomas Tschager
* Tomas Hruz
* Philipp Hoppen
*
* $Id$
*
* Changes
* -------
* 06-Feb-2015 : Initial revision (FB);
*
*/
package org.jgrapht.alg;
import java.util.*;
import java.util.Map.*;
import org.jgrapht.*;
import org.jgrapht.graph.*;
/**
* Clique Minimal Separator Decomposition using MCS-M+ and Atoms algorithm as
* described in Berry et al. An Introduction to Clique Minimal Separator
* Decomposition (2010), DOI:10.3390/a3020197,
* http://www.mdpi.com/1999-4893/3/2/197
*
* The Clique Minimal Separator (CMS) Decomposition is a procedure that
* splits a graph into a set of subgraphs separated by minimal clique
* separators, adding the separating clique to each component produced by the
* separation. At the end we have a set of atoms. The CMS decomposition is
* unique and yields the set of the atoms independent of the order of the
* decomposition.
*
* @author Florian Buenzli ([email protected])
* @author Thomas Tschager ([email protected])
* @author Tomas Hruz ([email protected])
* @author Philipp Hoppen
*/
public class CliqueMinimalSeparatorDecomposition
{
/**
* Source graph to operate on
*/
private UndirectedGraph graph;
/**
* Minimal triangulation of graph
*/
private UndirectedGraph chordalGraph;
/**
* Fill edges
*/
private Set fillEdges;
/**
* Minimal elimination ordering on the vertices of graph
*/
private LinkedList meo;
/**
* List of all vertices that generate a minimal separator of
* chordGraph
*/
private List generators;
/**
* Set of clique minimal separators
*/
private Set> separators;
/**
* The atoms generated by the decomposition
*/
private Set> atoms;
/**
* Map for each separator how many components it produces.
*/
private Map, Integer> fullComponentCount =
new HashMap, Integer>();
/**
* Setup a clique minimal separator decomposition on undirected graph
* g
. Loops and multiple edges are removed, i.e. the graph is
* transformed to a simple graph.
*
* @param g The graph to decompose.
*/
public CliqueMinimalSeparatorDecomposition(UndirectedGraph g)
{
this.graph = g;
this.fillEdges = new HashSet();
}
/**
* Compute the minimal triangulation of the graph. Implementation of
* Algorithm MCS-M+ as described in Berry et al. (2010),
* DOI:10.3390/a3020197
* http://www.mdpi.com/1999-4893/3/2/197
*/
private void computeMinimalTriangulation()
{
// initialize chordGraph with same vertices as graph
chordalGraph = new SimpleGraph(graph.getEdgeFactory());
for (V v : graph.vertexSet()) {
chordalGraph.addVertex(v);
}
// initialize g' as subgraph of graph (same vertices and edges)
final UndirectedGraph gprime = copyAsSimpleGraph(graph);
int s = -1;
generators = new ArrayList();
meo = new LinkedList();
final Map vertexLabels = new HashMap();
for (V v : gprime.vertexSet()) {
vertexLabels.put(v, 0);
}
for (int i = 1, n = graph.vertexSet().size(); i <= n; i++) {
V v = getMaxLabelVertex(vertexLabels);
LinkedList Y =
new LinkedList(
Graphs.neighborListOf(gprime, v));
if (vertexLabels.get(v) <= s) {
generators.add(v);
}
s = vertexLabels.get(v);
// Mark x reached and all other vertices of gprime unreached
HashSet reached = new HashSet();
reached.add(v);
// mark neighborhood of x reached and add to reach(label(y))
HashMap> reach =
new HashMap>();
// mark y reached and add y to reach
for (V y : Y) {
reached.add(y);
addToReach(vertexLabels.get(y), y, reach);
}
for (int j = 0; j < graph.vertexSet().size(); j++) {
if (!reach.containsKey(j)) {
continue;
}
while (reach.get(j).size() > 0) {
// remove a vertex y from reach(j)
V y = reach.get(j).iterator().next();
reach.get(j).remove(y);
for (V z : Graphs.neighborListOf(gprime, y)) {
if (!reached.contains(z)) {
reached.add(z);
if (vertexLabels.get(z) > j) {
Y.add(z);
E fillEdge =
graph.getEdgeFactory().createEdge(
v,
z);
fillEdges.add(fillEdge);
addToReach(vertexLabels.get(z), z, reach);
} else {
addToReach(j, z, reach);
}
}
}
}
}
for (V y : Y) {
chordalGraph.addEdge(v, y);
vertexLabels.put(y, vertexLabels.get(y) + 1);
}
meo.addLast(v);
gprime.removeVertex(v);
vertexLabels.remove(v);
}
}
/**
* Get the vertex with the maximal label.
*
* @param vertexLabels Map that gives a label for each vertex.
*
* @return Vertex with the maximal label.
*/
private V getMaxLabelVertex(Map vertexLabels)
{
Iterator> iterator =
vertexLabels.entrySet().iterator();
Entry max = iterator.next();
while (iterator.hasNext()) {
Entry e = iterator.next();
if (e.getValue() > max.getValue()) {
max = e;
}
}
return max.getKey();
}
/**
* Add a vertex to reach.
*
* @param k vertex' label
* @param v the vertex
* @param r the reach structure.
*/
private void addToReach(Integer k, V v, HashMap> r)
{
if (r.containsKey(k)) {
r.get(k).add(v);
} else {
HashSet set = new HashSet();
set.add(v);
r.put(k, set);
}
}
/**
* Compute the unique decomposition of the input graph G (atoms of G).
* Implementation of algorithm Atoms as described in Berry et al. (2010),
* DOI:10.3390/a3020197,
* http://www.mdpi.com/1999-4893/3/2/197
*/
private void computeAtoms()
{
if (chordalGraph == null) {
computeMinimalTriangulation();
}
separators = new HashSet>();
// initialize g' as subgraph of graph (same vertices and edges)
UndirectedGraph gprime = copyAsSimpleGraph(graph);
// initialize h' as subgraph of chordalGraph (same vertices and edges)
UndirectedGraph hprime = copyAsSimpleGraph(chordalGraph);
atoms = new HashSet>();
Iterator iterator = meo.descendingIterator();
while (iterator.hasNext()) {
V v = iterator.next();
if (generators.contains(v)) {
Set separator =
new HashSet(Graphs.neighborListOf(
hprime,
v));
if (isClique(graph, separator)) {
if (separator.size() > 0) {
if (separators.contains(separator)) {
fullComponentCount.put(
separator,
fullComponentCount.get(separator) + 1);
} else {
fullComponentCount.put(separator, 2);
separators.add(separator);
}
}
UndirectedGraph tmpGraph = copyAsSimpleGraph(gprime);
tmpGraph.removeAllVertices(separator);
ConnectivityInspector con =
new ConnectivityInspector(tmpGraph);
if (con.isGraphConnected()) {
throw new RuntimeException(
"separator did not separate the graph");
}
for (Set component : con.connectedSets()) {
if (component.contains(v)) {
gprime.removeAllVertices(component);
component.addAll(separator);
atoms.add(new HashSet(component));
assert (component.size() > 0);
break;
}
}
}
}
hprime.removeVertex(v);
}
if (gprime.vertexSet().size() > 0) {
atoms.add(new HashSet(gprime.vertexSet()));
}
}
/**
* Check whether the subgraph of graph
induced by the given
* vertices
is complete, i.e. a clique.
*
* @param graph the graph.
* @param vertices the vertices to induce the subgraph from.
*
* @return true if the induced subgraph is a clique.
*/
private static boolean isClique(
UndirectedGraph graph,
Set vertices)
{
for (V v1 : vertices) {
for (V v2 : vertices) {
if ((v1 != v2) && (graph.getEdge(v1, v2) == null)) {
return false;
}
}
}
return true;
}
/**
* Create a copy of a graph for internal use.
*
* @param graph the graph to copy.
*
* @return A copy of the graph projected to a SimpleGraph.
*/
private static UndirectedGraph copyAsSimpleGraph(
UndirectedGraph graph)
{
UndirectedGraph copy =
new SimpleGraph(
graph.getEdgeFactory());
if (graph instanceof SimpleGraph) {
Graphs.addGraph(copy, graph);
} else {
// project graph to SimpleGraph
Graphs.addAllVertices(copy, graph.vertexSet());
for (E e : graph.edgeSet()) {
V v1 = graph.getEdgeSource(e);
V v2 = graph.getEdgeTarget(e);
if ((v1 != v2) && !copy.containsEdge(e)) {
copy.addEdge(v1, v2);
}
}
}
return copy;
}
/**
* Check if the graph is chordal.
*
* @return true if the graph is chordal, false otherwise.
*/
public boolean isChordal()
{
if (chordalGraph == null) {
computeMinimalTriangulation();
}
return (chordalGraph.edgeSet().size() == graph.edgeSet().size());
}
/**
* Get the fill edges generated by the triangulation.
*
* @return Set of fill edges.
*/
public Set getFillEdges()
{
if (fillEdges == null) {
computeMinimalTriangulation();
}
return fillEdges;
}
/**
* Get the minimal triangulation of the graph.
*
* @return Triangulated graph.
*/
public UndirectedGraph getMinimalTriangulation()
{
if (chordalGraph == null) {
computeMinimalTriangulation();
}
return chordalGraph;
}
/**
* Get the generators of the separators of the triangulated graph, i.e. all
* vertices that generate a minimal separator of triangulated graph.
*
* @return List of generators.
*/
public List getGenerators()
{
if (generators == null) {
computeMinimalTriangulation();
}
return generators;
}
/**
* Get the minimal elimination ordering produced by the triangulation.
*
* @return The minimal elimination ordering.
*/
public LinkedList getMeo()
{
if (meo == null) {
computeMinimalTriangulation();
}
return meo;
}
/**
* Get a map to know for each separator how many components it produces.
*
* @return A map from separators to integers (component count).
*/
public Map, Integer> getFullComponentCount()
{
if (fullComponentCount == null) {
computeAtoms();
}
return fullComponentCount;
}
/**
* Get the atoms generated by the decomposition.
*
* @return Set of atoms, where each atom is described as the set of its
* vertices.
*/
public Set> getAtoms()
{
if (atoms == null) {
computeAtoms();
}
return atoms;
}
/**
* Get the clique minimal separators.
*
* @return Set of separators, where each separator is described as the set
* of its vertices.
*/
public Set> getSeparators()
{
if (separators == null) {
computeAtoms();
}
return separators;
}
/**
* Get the original graph.
*
* @return Original graph.
*/
public UndirectedGraph getGraph()
{
return graph;
}
}
// End CliqueMinimalSeparatorDecomposition.java