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

cdc.graphs.core.GraphCycles Maven / Gradle / Ivy

There is a newer version: 0.71.2
Show newest version
package cdc.graphs.core;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import cdc.graphs.EdgeDirection;
import cdc.graphs.EdgeTip;
import cdc.graphs.GraphAdapter;
import cdc.graphs.NodeConnectivity;
import cdc.graphs.impl.ExplicitSubGraph;
import cdc.graphs.impl.RestrictionSubGraph;
import cdc.util.function.Evaluation;
import cdc.util.function.Evaluator;
import cdc.util.function.Visitor;

/**
 * Set of algorithms related to cycles.
 *
 * @author Damien Carbonne
 *
 * @param  Node class
 * @param  Edge class
 */
public class GraphCycles extends GraphBase {
    public GraphCycles(GraphAdapter adapter) {
        super(adapter);
    }

    /**
     * @return whether the graph contains cycles or not.
     */
    public boolean containsCycles() {
        final CycleDetector internal = new CycleDetector<>(adapter);
        return internal.eval();
    }

    /**
     * Returns {@code true} when 2 nodes are connected.
     *
     * @param source The source node.
     * @param target The target node.
     * @return {@code true} when {@code source} is connected to {@code target}.
     */
    public boolean areConnected(N source,
                                N target) {
        final ConnectionDetector detector = new ConnectionDetector<>(adapter, source, target);
        return detector.areConnected();
    }

    /**
     * Returns whether a particular node is member of a cycle or not.
     *
     * @param node The tested node.
     * @return whether node is member of a cycle or not.
     */
    public boolean nodeIsCycleMember(N node) {
        return areConnected(node, node);
    }

    public boolean edgeIsCycleMember(E edge) {
        final N src = adapter.getTip(edge, EdgeTip.SOURCE);
        final N tgt = adapter.getTip(edge, EdgeTip.TARGET);
        return areConnected(tgt, src);
    }

    /**
     * Returns the subgraph that contains all nodes and edges that are
     * participating to cycles.
     *
     * @return the subgraph of cycle members.
     */
    public ExplicitSubGraph computeCyclesMembers() {
        final RestrictionSubGraph subgraph = new RestrictionSubGraph<>(adapter);
        final HashSet current = new HashSet<>();
        final HashSet next = new HashSet<>();

        for (final N node : subgraph.getNodes()) {
            if (subgraph.getConnectivity(node) != NodeConnectivity.IN_OUT) {
                current.add(node);
            }
        }

        while (!current.isEmpty()) {
            // Computes set of nodes that are in relation with nodes of current
            next.clear();
            for (final N node : current) {
                for (final E edge : subgraph.getEdges(node, EdgeDirection.OUTGOING)) {
                    next.add(subgraph.getTip(edge, EdgeTip.TARGET));
                }
                for (final E edge : subgraph.getEdges(node, EdgeDirection.INGOING)) {
                    next.add(subgraph.getTip(edge, EdgeTip.SOURCE));
                }
            }

            // Remove nodes in current
            for (final N node : current) {
                subgraph.removeNode(node);
            }

            next.removeAll(subgraph.getRemovedNodes());
            current.clear();
            for (final N node : next) {
                if (subgraph.getConnectivity(node) != NodeConnectivity.IN_OUT) {
                    current.add(node);
                }
            }
        }

        detectNonMemberNodes(subgraph);
        detectNonMemberEdges(subgraph);

        return subgraph;
    }

    private void detectNonMemberNodes(RestrictionSubGraph subgraph) {
        final Set nonMemberNodes = new HashSet<>();
        for (final N node : subgraph.getNodes()) {
            if (!nodeIsCycleMember(node)) {
                nonMemberNodes.add(node);
            }
        }
        for (final N node : nonMemberNodes) {
            subgraph.removeNode(node);
        }
    }

    private void detectNonMemberEdges(RestrictionSubGraph subgraph) {
        final Set nonMemberEdges = new HashSet<>();
        for (final E edge : subgraph.getEdges()) {
            if (!edgeIsCycleMember(edge)) {
                nonMemberEdges.add(edge);
            }
        }
        for (final E edge : nonMemberEdges) {
            subgraph.removeEdge(edge);
        }
    }

    /**
     * Computes the set of nodes and edges that are participating to cycles.
     *
     * @param nodes Collection of nodes that will be filled with nodes participating to a cycle.
     *            This collection is cleared on startup.
     * @param edges Collection of edges that will be filled with edges participating to a cycle.
     *            This collection is cleared on startup.
     */
    public void computeCyclesMembers(Collection nodes,
                                     Collection edges) {
        final ExplicitSubGraph subgraph = computeCyclesMembers();

        // All remaining nodes and edges are participating to cycles.
        nodes.clear();
        for (final N node : subgraph.getNodes()) {
            nodes.add(node);
        }
        edges.clear();
        for (final E edge : subgraph.getEdges()) {
            edges.add(edge);
        }
    }

    /**
     * Class dedicated to detection of cycles in a graph.
     *
     * @author Damien Carbonne
     * @param  The node type.
     * @param  The edge type.
     */
    private static class CycleDetector extends GraphBase {
        private final Set visited = new HashSet<>();
        private final Set critical = new HashSet<>();

        public CycleDetector(GraphAdapter adapter) {
            super(adapter);
        }

        public boolean eval() {
            visited.clear();
            critical.clear();
            for (final N node : adapter.getNodes()) {
                if (!visited.contains(node)) {
                    final boolean found = visit(node);
                    if (found) {
                        return true;
                    }
                }
            }
            return false;
        }

        private final boolean visit(final N node) {
            if (critical.contains(node)) {
                return true;
            } else {
                critical.add(node);
                for (final E edge : adapter.getEdges(node, EdgeDirection.OUTGOING)) {
                    final N successor = adapter.getTip(edge, EdgeTip.TARGET);
                    final boolean found = visit(successor);
                    if (found) {
                        return true;
                    }
                }
                critical.remove(node);
                visited.add(node);
                return false;
            }
        }
    }

    private static class ConnectionDetector extends GraphBase {
        boolean found = false;
        private final Evaluator evaluator;

        public ConnectionDetector(GraphAdapter adapter,
                                  N source,
                                  N target) {
            super(adapter);
            this.evaluator = item -> {
                if (adapter.hasEdge(item, target)) {
                    found = true;
                    return Evaluation.PRUNE;
                } else {
                    return Evaluation.CONTINUE;
                }
            };

            final GraphTraverser traverser = new GraphTraverser<>(adapter);
            traverser.traverseDepthFirstPre(source,
                                            EdgeDirection.OUTGOING,
                                            Visitor.ignore(),
                                            evaluator);
        }

        public final boolean areConnected() {
            return found;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy