org.jgrapht.alg.TransitiveReduction Maven / Gradle / Ivy
/*
* (C) Copyright 2015-2021, by Christophe Thiebaud 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;
import org.jgrapht.*;
import java.util.*;
/**
* An implementation of Harry Hsu's
* transitive reduction algorithm.
*
*
*
*
* This is a port from a python example by Michael Clerx, posted as an answer to a question about
*
* transitive reduction algorithm pseudocode on Stack
* Overflow
*
*
* @author Christophe Thiebaud
*/
public class TransitiveReduction
{
/**
* Singleton instance.
*/
public static final TransitiveReduction INSTANCE = new TransitiveReduction();
/**
* Private Constructor.
*/
private TransitiveReduction()
{
}
/**
* The matrix passed as input parameter will be transformed into a path matrix.
*
*
* This method is package visible for unit testing, but it is meant as a private method.
*
*
* @param matrix the original matrix to transform into a path matrix
*/
static void transformToPathMatrix(BitSet[] matrix)
{
// compute path matrix
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix.length; j++) {
if (i == j) {
continue;
}
if (matrix[j].get(i)) {
for (int k = 0; k < matrix.length; k++) {
if (!matrix[j].get(k)) {
matrix[j].set(k, matrix[i].get(k));
}
}
}
}
}
}
/**
* The path matrix passed as input parameter will be transformed into a transitively reduced
* matrix.
*
*
* This method is package visible for unit testing, but it is meant as a private method.
*
*
* @param pathMatrix the path matrix to reduce
*/
static void transitiveReduction(BitSet[] pathMatrix)
{
// transitively reduce
for (int j = 0; j < pathMatrix.length; j++) {
for (int i = 0; i < pathMatrix.length; i++) {
if (pathMatrix[i].get(j)) {
for (int k = 0; k < pathMatrix.length; k++) {
if (pathMatrix[j].get(k)) {
pathMatrix[i].set(k, false);
}
}
}
}
}
}
/**
* This method will remove all transitive edges from the graph passed as input parameter.
*
*
* You may want to clone the graph before, as transitive edges will be pitilessly removed.
*
*
* e.g.
*
*
* {
* @code DirectedGraph<V, T> soonToBePrunedDirectedGraph;
*
* TransitiveReduction.INSTANCE.reduce(soonToBePrunedDirectedGraph);
*
* // pruned !
* }
*
*
* @param directedGraph the directed graph that will be reduced transitively
* @param the graph vertex type
* @param the graph edge type
*/
public void reduce(final Graph directedGraph)
{
GraphTests.requireDirected(directedGraph, "Graph must be directed");
final List vertices = new ArrayList<>(directedGraph.vertexSet());
final int n = vertices.size();
BitSet[] originalMatrix = new BitSet[n];
for (int i = 0; i < originalMatrix.length; i++) {
originalMatrix[i] = new BitSet(n);
}
// initialize matrix with zeros
// 'By default, all bits in the set initially have the value false.'
// cf. http://docs.oracle.com/javase/7/docs/api/java/util/BitSet.html
// initialize matrix with edges
for (final E edge : directedGraph.edgeSet()) {
final V v1 = directedGraph.getEdgeSource(edge);
final V v2 = directedGraph.getEdgeTarget(edge);
final int i1 = vertices.indexOf(v1);
final int i2 = vertices.indexOf(v2);
originalMatrix[i1].set(i2);
}
// create path matrix from original matrix
final BitSet[] pathMatrix = originalMatrix;
transformToPathMatrix(pathMatrix);
// create reduced matrix from path matrix
final BitSet[] transitivelyReducedMatrix = pathMatrix;
transitiveReduction(transitivelyReducedMatrix);
// remove edges from the DirectedGraph which are not in the reduced
// matrix
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (!transitivelyReducedMatrix[i].get(j)) {
directedGraph
.removeEdge(directedGraph.getEdge(vertices.get(i), vertices.get(j)));
}
}
}
}
}