All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrapht.alg.clique.CliqueMinimalSeparatorDecomposition Maven / Gradle / Ivy

The newest version!
/*
 * (C) Copyright 2015-2023, by Florian Buenzli 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.clique;

import org.jgrapht.*;
import org.jgrapht.alg.connectivity.*;
import org.jgrapht.graph.builder.*;

import java.util.*;
import java.util.Map.*;

/**
 * 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. * * @param the graph vertex type * @param the graph edge type * * @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 Graph graph; /** * Minimal triangulation of graph */ private Graph 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<>(); /** * Setup a clique minimal separator decomposition on undirected graph * g. Loops and multiple (parallel) edges are removed, i.e. the graph is transformed to a * simple graph. * * @param g The graph to decompose. */ public CliqueMinimalSeparatorDecomposition(Graph g) { this.graph = GraphTests.requireUndirected(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 = GraphTypeBuilder . undirected().edgeSupplier(graph.getEdgeSupplier()) .vertexSupplier(graph.getVertexSupplier()).allowingMultipleEdges(false) .allowingSelfLoops(false).buildGraph(); for (V v : graph.vertexSet()) { chordalGraph.addVertex(v); } // initialize g' as subgraph of graph (same vertices and edges) final Graph 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 neighborsY = 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 : neighborsY) { 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) { neighborsY.add(z); E fillEdge = graph.getEdgeSupplier().get(); fillEdges.add(fillEdge); addToReach(vertexLabels.get(z), z, reach); } else { addToReach(j, z, reach); } } } } } for (V y : neighborsY) { 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) Graph gprime = copyAsSimpleGraph(graph); // initialize h' as subgraph of chordalGraph (same vertices and edges) Graph 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); } } Graph tmpGraph = copyAsSimpleGraph(gprime); tmpGraph.removeAllVertices(separator); ConnectivityInspector con = new ConnectivityInspector<>(tmpGraph); if (con.isConnected()) { 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(Graph graph, Set vertices) { for (V v1 : vertices) { for (V v2 : vertices) { if (!v1.equals(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 Graph copyAsSimpleGraph(Graph graph) { Graph copy = GraphTypeBuilder . undirected().edgeSupplier(graph.getEdgeSupplier()) .vertexSupplier(graph.getVertexSupplier()).allowingMultipleEdges(false) .allowingSelfLoops(false).buildGraph(); if (graph.getType().isSimple()) { 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.equals(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 Graph 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 Graph getGraph() { return graph; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy