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

org.neo4j.graphalgo.impl.shortestpath.FloydWarshall Maven / Gradle / Ivy

There is a newer version: 5.26.1
Show newest version
/*
 * Copyright (c) 2002-2016 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.graphalgo.impl.shortestpath;

import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.neo4j.graphalgo.CostAccumulator;
import org.neo4j.graphalgo.CostEvaluator;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;

/**
 * This provides an implementation of the Floyd Warshall algorithm solving the
 * all pair shortest path problem.
 * @complexity The {@link CostEvaluator} is called once for every relationship.
 *             The {@link CostAccumulator} and cost comparator are both called
 *             n^3 times. Assuming they run in constant time, the time
 *             complexity for this algorithm is O(n^3).
 * @author Patrik Larsson
 * @param 
 *            The datatype the edge weights are represented by.
 */
public class FloydWarshall
{
    protected CostType startCost; // starting cost for all nodes
    protected CostType infinitelyBad; // starting value for calculation
    protected Direction relationDirection;
    protected CostEvaluator costEvaluator = null;
    protected CostAccumulator costAccumulator = null;
    protected Comparator costComparator = null;
    protected Set nodeSet;
    protected Set relationshipSet;
    CostType[][] costMatrix;
    Integer[][] predecessors;
    Map nodeIndexes; // node ->index
    Node[] IndexedNodes; // index -> node
    protected boolean doneCalculation = false;

    /**
     * @param startCost
     *            The cost for just starting (or ending) a path in a node.
     * @param infinitelyBad
     *            A cost worse than all others. This is used to initialize the
     *            distance matrix.
     * @param costRelationType
     *            The relationship type to traverse.
     * @param relationDirection
     *            The direction in which the paths should follow the
     *            relationships.
     * @param costEvaluator
     * @see {@link CostEvaluator}
     * @param costAccumulator
     * @see {@link CostAccumulator}
     * @param costComparator
     * @see {@link CostAccumulator} or {@link CostEvaluator}
     * @param nodeSet
     *            The set of nodes the calculation should be run on.
     * @param relationshipSet
     *            The set of relationships that should be processed.
     */
    public FloydWarshall( CostType startCost, CostType infinitelyBad,
        Direction relationDirection, CostEvaluator costEvaluator,
        CostAccumulator costAccumulator,
        Comparator costComparator, Set nodeSet,
        Set relationshipSet )
    {
        super();
        this.startCost = startCost;
        this.infinitelyBad = infinitelyBad;
        this.relationDirection = relationDirection;
        this.costEvaluator = costEvaluator;
        this.costAccumulator = costAccumulator;
        this.costComparator = costComparator;
        this.nodeSet = nodeSet;
        this.relationshipSet = relationshipSet;
    }

    /**
     * This resets the calculation if we for some reason would like to redo it.
     */
    public void reset()
    {
        doneCalculation = false;
    }

    /**
     * Internal calculate method that will do the calculation. This can however
     * be called externally to manually trigger the calculation.
     */
    @SuppressWarnings( "unchecked" )
    public void calculate()
    {
        // Don't do it more than once
        if ( doneCalculation )
        {
            return;
        }
        doneCalculation = true;
        // Build initial matrix
        int n = nodeSet.size();
        costMatrix = (CostType[][]) new Object[n][n];
        predecessors = new Integer[n][n];
        IndexedNodes = new Node[n];
        nodeIndexes = new HashMap();
        for ( int i = 0; i < n; ++i )
        {
            for ( int j = 0; j < n; ++j )
            {
                costMatrix[i][j] = infinitelyBad;
            }
            costMatrix[i][i] = startCost;
        }
        int nodeIndex = 0;
        for ( Node node : nodeSet )
        {
            nodeIndexes.put( node, nodeIndex );
            IndexedNodes[nodeIndex] = node;
            ++nodeIndex;
        }
        // Put the relationships in there
        for ( Relationship relationship : relationshipSet )
        {
            Integer i1 = nodeIndexes.get( relationship.getStartNode() );
            Integer i2 = nodeIndexes.get( relationship.getEndNode() );
            if ( i1 == null || i2 == null )
            {
                // TODO: what to do here? pretend nothing happened? cast
                // exception?
                continue;
            }
            if ( relationDirection.equals( Direction.BOTH )
                || relationDirection.equals( Direction.OUTGOING ) )
            {
                costMatrix[i1][i2] = costEvaluator
.getCost( relationship,
                        Direction.OUTGOING );
                predecessors[i1][i2] = i1;
            }
            if ( relationDirection.equals( Direction.BOTH )
                || relationDirection.equals( Direction.INCOMING ) )
            {
                costMatrix[i2][i1] = costEvaluator.getCost( relationship,
                        Direction.INCOMING );
                predecessors[i2][i1] = i2;
            }
        }
        // Do it!
        for ( int v = 0; v < n; ++v )
        {
            for ( int i = 0; i < n; ++i )
            {
                for ( int j = 0; j < n; ++j )
                {
                    CostType alternative = costAccumulator.addCosts(
                        costMatrix[i][v], costMatrix[v][j] );
                    if ( costComparator.compare( costMatrix[i][j], alternative ) > 0 )
                    {
                        costMatrix[i][j] = alternative;
                        predecessors[i][j] = predecessors[v][j];
                    }
                }
            }
        }
        // TODO: detect negative cycles?
    }

    /**
     * This returns the cost for the shortest path between two nodes.
     * @param node1
     *            The start node.
     * @param node2
     *            The end node.
     * @return The cost for the shortest path.
     */
    public CostType getCost( Node node1, Node node2 )
    {
        calculate();
        return costMatrix[nodeIndexes.get( node1 )][nodeIndexes.get( node2 )];
    }

    /**
     * This returns the shortest path between two nodes as list of nodes.
     * @param startNode
     *            The start node.
     * @param targetNode
     *            The end node.
     * @return The shortest path as a list of nodes.
     */
    public List getPath( Node startNode, Node targetNode )
    {
        calculate();
        LinkedList path = new LinkedList();
        int index = nodeIndexes.get( targetNode );
        int startIndex = nodeIndexes.get( startNode );
        Node n = targetNode;
        while ( !n.equals( startNode ) )
        {
            path.addFirst( n );
            index = predecessors[startIndex][index];
            n = IndexedNodes[index];
        }
        path.addFirst( n );
        return path;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy