/* Copyright 2008-2010 Gephi Authors : Mathieu Bastian <mathieu.bastian@gephi.org> Website : http://www.gephi.org This file is part of Gephi. Gephi 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. Gephi 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 Gephi. If not, see <http://www.gnu.org/licenses/>. */ package org.gephi.algorithms.shortestpath; import java.util.HashMap; import org.gephi.data.attributes.type.TimeInterval; import org.gephi.dynamic.DynamicUtilities; import org.gephi.dynamic.api.DynamicController; import org.gephi.graph.api.DirectedGraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Node; import org.openide.util.Lookup; /** * * @author Mathieu Bastian */ public class BellmanFordShortestPathAlgorithm extends AbstractShortestPathAlgorithm { protected final DirectedGraph graph; protected final HashMap<Node, Edge> predecessors; protected TimeInterval timeInterval; public BellmanFordShortestPathAlgorithm(DirectedGraph graph, Node sourceNode) { super(sourceNode); this.graph = graph; predecessors = new HashMap<Node, Edge>(); DynamicController dynamicController = Lookup.getDefault().lookup(DynamicController.class); if (dynamicController != null) { timeInterval = DynamicUtilities.getVisibleInterval(dynamicController.getModel(graph.getGraphModel().getWorkspace())); } } public void compute() { graph.readLock(); //Initialize int nodeCount = 0; for (Node node : graph.getNodes()) { distances.put(node, Double.POSITIVE_INFINITY); nodeCount++; } distances.put(sourceNode, 0d); //Relax edges repeatedly for (int i = 0; i < nodeCount; i++) { boolean relaxed = false; for (Edge edge : graph.getEdges()) { Node target = edge.getTarget(); if (relax(edge)) { relaxed = true; predecessors.put(target, edge); } } if (!relaxed) { break; } } //Check for negative-weight cycles for (Edge edge : graph.getEdges()) { if (distances.get(edge.getSource()) + edgeWeight(edge) < distances.get(edge.getTarget())) { graph.readUnlock(); throw new RuntimeException("The Graph contains a negative-weighted cycle"); } } graph.readUnlock(); } @Override protected double edgeWeight(Edge edge) { if (timeInterval != null) { return edge.getWeight(timeInterval.getLow(), timeInterval.getHigh()); } return edge.getWeight(); } public Node getPredecessor(Node node) { Edge edge = predecessors.get(node); if (edge != null) { if (edge.getSource() != node) { return edge.getSource(); } else { return edge.getTarget(); } } return null; } public Edge getPredecessorIncoming(Node node) { return predecessors.get(node); } }