grakn.core.graql.reasoner.utils.TarjanSCC.java Source code

Java tutorial

Introduction

Here is the source code for grakn.core.graql.reasoner.utils.TarjanSCC.java

Source

/*
 * GRAKN.AI - THE KNOWLEDGE GRAPH
 * Copyright (C) 2018 Grakn Labs Ltd
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package grakn.core.graql.reasoner.utils;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.stream.Collectors;

/**
 * Tarjan's Strongly Connected Components algorithm
 * https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
 *
 * Here used to find cycles in the graph.
 *
 * @param <T> type of the graph node
 */
public class TarjanSCC<T> {

    private final Set<T> visited = new HashSet<>();
    private final Stack<T> stack = new Stack<>();
    private final HashMap<T, Integer> lowLink = new HashMap<>();

    private final List<Set<T>> scc = new ArrayList<>();
    private int pre = 0;

    private final HashMultimap<T, T> graph;

    public TarjanSCC(HashMultimap<T, T> graph) {
        this.graph = graph;
        for (T node : graph.keySet()) {
            if (!visited.contains(node))
                dfs(node);
        }
    }

    public List<Set<T>> getSCC() {
        return scc;
    }

    /**
     * A cycle isa a connected component that has more than one vertex or a single vertex linked to itself.
     * @return list of cycles in the graph
     */
    public List<Set<T>> getCycles() {
        return scc.stream()
                .filter(cc -> cc.size() > 1
                        || graph.get(Iterables.getOnlyElement(cc)).contains(Iterables.getOnlyElement(cc)))
                .collect(Collectors.toList());
    }

    private void dfs(T node) {
        visited.add(node);
        lowLink.put(node, pre++);
        int min = lowLink.get(node);
        stack.push(node);
        //look at neighbours of v
        for (T n : graph.get(node)) {
            if (!visited.contains(n))
                dfs(n);
            if (lowLink.get(n) < min)
                min = lowLink.get(n);
        }
        if (min < lowLink.get(node)) {
            lowLink.put(node, min);
            return;
        }
        T w;
        Set<T> component = new HashSet<>();
        do {
            w = stack.pop();
            component.add(w);
            lowLink.put(w, graph.keySet().size());
        } while (w != node);
        scc.add(component);
    }
}