/*
* Copyright 2008 Network Engine for Objects in Lund AB [neotechnology.com]
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.graphalgo.shortestpath;
import static org.junit.Assert.assertTrue;
import java.util.List;
import org.junit.Test;
import org.neo4j.graphalgo.CommonEvaluators;
import org.neo4j.graphalgo.CostEvaluator;
import org.neo4j.graphalgo.impl.shortestpath.Dijkstra;
import org.neo4j.graphalgo.impl.util.DoubleAdder;
import org.neo4j.graphalgo.impl.util.DoubleComparator;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
import common.Neo4jAlgoTestCase;
import common.SimpleGraphBuilder;
public class DijkstraMultiplePathsTest extends Neo4jAlgoTestCase
{
protected Dijkstra<Double> getDijkstra( SimpleGraphBuilder graph,
Double startCost, String startNode, String endNode )
{
return new Dijkstra<Double>( startCost, graph.getNode( startNode ),
graph.getNode( endNode ),
CommonEvaluators.doubleCostEvaluator( "cost" ),
new org.neo4j.graphalgo.impl.util.DoubleAdder(),
new org.neo4j.graphalgo.impl.util.DoubleComparator(),
Direction.BOTH, MyRelTypes.R1 );
}
/**
* A triangle with 0 cost should generate two paths between every pair of
* nodes.
*/
@Test
public void testTriangle()
{
graph.makeEdge( "a", "b", "cost", (double) 0 );
graph.makeEdge( "b", "c", "cost", (double) 0 );
graph.makeEdge( "c", "a", "cost", (double) 0 );
Dijkstra<Double> dijkstra;
String[] nodes = { "a", "b", "c" };
for ( String node1 : nodes )
{
for ( String node2 : nodes )
{
dijkstra = getDijkstra( graph, 0.0, node1, node2 );
int nrPaths = dijkstra.getPathsAsNodes().size();
if ( !node1.equals( node2 ) )
{
assertTrue( "Number of paths (" + node1 + "->" + node2
+ "): " + nrPaths, nrPaths == 2 );
}
assertTrue( dijkstra.getCost() == 0.0 );
}
}
}
/**
* From each direction 2 ways are possible so 4 ways should be the total.
*/
@Test
public void test1()
{
graph.makeEdge( "a", "b", "cost", (double) 1 );
graph.makeEdge( "b", "d", "cost", (double) 1 );
graph.makeEdge( "a", "c", "cost", (double) 1 );
graph.makeEdge( "c", "d", "cost", (double) 1 );
graph.makeEdge( "d", "e", "cost", (double) 1 );
graph.makeEdge( "e", "f", "cost", (double) 1 );
graph.makeEdge( "f", "h", "cost", (double) 1 );
graph.makeEdge( "e", "g", "cost", (double) 1 );
graph.makeEdge( "g", "h", "cost", (double) 1 );
Dijkstra<Double> dijkstra = getDijkstra( graph, 0.0, "a", "h" );
assertTrue( dijkstra.getPaths().size() == 4 );
assertTrue( dijkstra.getPathsAsNodes().size() == 4 );
assertTrue( dijkstra.getPathsAsRelationships().size() == 4 );
assertTrue( dijkstra.getCost() == 5.0 );
}
/**
* Two different ways. This is supposed to test when the traversers meet in
* several places.
*/
@Test
public void test2()
{
graph.makeEdge( "a", "b", "cost", (double) 1 );
graph.makeEdge( "a", "f", "cost", (double) 1 );
graph.makeEdge( "b", "c", "cost", (double) 1 );
graph.makeEdge( "f", "g", "cost", (double) 1 );
graph.makeEdge( "c", "d", "cost", (double) 1 );
graph.makeEdge( "g", "h", "cost", (double) 1 );
graph.makeEdge( "d", "e", "cost", (double) 1 );
graph.makeEdge( "h", "e", "cost", (double) 1 );
Dijkstra<Double> dijkstra = getDijkstra( graph, 0.0, "a", "e" );
assertTrue( dijkstra.getPaths().size() == 2 );
assertTrue( dijkstra.getPathsAsNodes().size() == 2 );
assertTrue( dijkstra.getPathsAsRelationships().size() == 2 );
assertTrue( dijkstra.getCost() == 4.0 );
}
/**
* One side finding several paths to one node previously visited by the
* other side. The other side is kept busy with a chain of cost zero.
*/
@Test
public void test3()
{
// "zero" side
graph.makeEdge( "a", "b", "cost", (double) 0 );
graph.makeEdge( "b", "c", "cost", (double) 0 );
graph.makeEdge( "c", "d", "cost", (double) 0 );
graph.makeEdge( "d", "e", "cost", (double) 0 );
graph.makeEdge( "e", "f", "cost", (double) 0 );
graph.makeEdge( "f", "g", "cost", (double) 0 );
graph.makeEdge( "g", "h", "cost", (double) 0 );
graph.makeEdge( "h", "i", "cost", (double) 0 );
graph.makeEdge( "i", "j", "cost", (double) 0 );
graph.makeEdge( "j", "k", "cost", (double) 0 );
// "discovering" side
graph.makeEdge( "z", "y", "cost", (double) 0 );
graph.makeEdge( "y", "x", "cost", (double) 0 );
graph.makeEdge( "x", "w", "cost", (double) 0 );
graph.makeEdge( "w", "b", "cost", (double) 1 );
graph.makeEdge( "x", "b", "cost", (double) 2 );
graph.makeEdge( "y", "b", "cost", (double) 1 );
graph.makeEdge( "z", "b", "cost", (double) 1 );
graph.makeEdge( "zz", "z", "cost", (double) 0 );
Dijkstra<Double> dijkstra = getDijkstra( graph, 0.0, "a", "zz" );
assertTrue( dijkstra.getPathsAsNodes().size() == 3 );
assertTrue( dijkstra.getCost() == 1.0 );
}
/**
* another variant of the test above, but the discovering is a bit mixed.
*/
@Test
public void test4()
{
// "zero" side
graph.makeEdge( "a", "b", "cost", (double) 0 );
graph.makeEdge( "b", "c", "cost", (double) 0 );
graph.makeEdge( "c", "d", "cost", (double) 0 );
graph.makeEdge( "d", "e", "cost", (double) 0 );
graph.makeEdge( "e", "f", "cost", (double) 0 );
graph.makeEdge( "f", "g", "cost", (double) 0 );
graph.makeEdge( "g", "h", "cost", (double) 0 );
graph.makeEdge( "h", "i", "cost", (double) 0 );
graph.makeEdge( "i", "j", "cost", (double) 0 );
graph.makeEdge( "j", "k", "cost", (double) 0 );
// "discovering" side
graph.makeEdge( "z", "y", "cost", (double) 0 );
graph.makeEdge( "y", "x", "cost", (double) 0 );
graph.makeEdge( "x", "w", "cost", (double) 0 );
graph.makeEdge( "w", "b", "cost", (double) 1 );
graph.makeEdge( "x", "b", "cost", (double) 2 );
graph.makeEdge( "y", "b", "cost", (double) 1 );
graph.makeEdge( "z", "b", "cost", (double) 1 );
Dijkstra<Double> dijkstra = getDijkstra( graph, 0.0, "a", "z" );
assertTrue( dijkstra.getPathsAsNodes().size() == 3 );
assertTrue( dijkstra.getCost() == 1.0 );
}
/**
* "Diamond" shape, with some weights to resemble the test case above.
*/
@Test
public void test5()
{
graph.makeEdge( "a", "b", "cost", (double) 0 );
graph.makeEdge( "z", "y", "cost", (double) 0 );
graph.makeEdge( "y", "b", "cost", (double) 1 );
graph.makeEdge( "z", "b", "cost", (double) 1 );
graph.makeEdge( "y", "a", "cost", (double) 1 );
Dijkstra<Double> dijkstra = getDijkstra( graph, 0.0, "a", "z" );
List<List<Node>> paths = dijkstra.getPathsAsNodes();
assertTrue( paths.size() == 3 );
assertTrue( dijkstra.getCost() == 1.0 );
}
@Test
public void test6()
{
graph.makeEdgeChain( "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,z", "cost",
(double) 1 );
graph.makeEdge( "a", "b2", "cost", (double) 4 );
graph.makeEdge( "b2", "c", "cost", (double) -2 );
Dijkstra<Double> dijkstra = new Dijkstra<Double>( 0.0, graph
.getNode( "a" ), graph.getNode( "z" ),
CommonEvaluators.doubleCostEvaluator( "cost" ),
new org.neo4j.graphalgo.impl.util.DoubleAdder(),
new org.neo4j.graphalgo.impl.util.DoubleComparator(),
Direction.OUTGOING, MyRelTypes.R1 );
List<List<Node>> paths = dijkstra.getPathsAsNodes();
assertTrue( paths.size() == 2 );
}
@Test
public void test7()
{
Relationship edgeAB = graph.makeEdge( "a", "b" );
Relationship edgeBC = graph.makeEdge( "b", "c" );
Relationship edgeCD = graph.makeEdge( "c", "d" );
Relationship edgeDE = graph.makeEdge( "d", "e" );
Relationship edgeAB2 = graph.makeEdge( "a", "b2" );
Relationship edgeB2C = graph.makeEdge( "b2", "c" );
Relationship edgeCD2 = graph.makeEdge( "c", "d2" );
Relationship edgeD2E = graph.makeEdge( "d2", "e" );
Dijkstra<Double> dijkstra = new Dijkstra<Double>(
0.0,
graph.getNode( "a" ),
graph.getNode( "e" ),
new CostEvaluator<Double>()
{
public Double getCost( Relationship relationship,
Direction direction )
{
return 1.0;
}
}, new DoubleAdder(), new DoubleComparator(), Direction.OUTGOING,
MyRelTypes.R1 );
// path discovery flags
boolean pathBD = false;
boolean pathB2D = false;
boolean pathBD2 = false;
boolean pathB2D2 = false;
List<List<PropertyContainer>> paths = dijkstra.getPaths();
assertTrue( paths.size() == 4 );
for ( List<PropertyContainer> path : paths )
{
assertTrue( path.size() == 9 );
assertTrue( path.get( 0 ).equals( graph.getNode( "a" ) ) );
assertTrue( path.get( 4 ).equals( graph.getNode( "c" ) ) );
assertTrue( path.get( 8 ).equals( graph.getNode( "e" ) ) );
// first choice
if ( path.get( 2 ).equals( graph.getNode( "b" ) ) )
{
assertTrue( path.get( 1 ).equals( edgeAB ) );
assertTrue( path.get( 3 ).equals( edgeBC ) );
}
else
{
assertTrue( path.get( 1 ).equals( edgeAB2 ) );
assertTrue( path.get( 2 ).equals( graph.getNode( "b2" ) ) );
assertTrue( path.get( 3 ).equals( edgeB2C ) );
}
// second choice
if ( path.get( 6 ).equals( graph.getNode( "d" ) ) )
{
assertTrue( path.get( 5 ).equals( edgeCD ) );
assertTrue( path.get( 7 ).equals( edgeDE ) );
}
else
{
assertTrue( path.get( 5 ).equals( edgeCD2 ) );
assertTrue( path.get( 6 ).equals( graph.getNode( "d2" ) ) );
assertTrue( path.get( 7 ).equals( edgeD2E ) );
}
// combinations
if ( path.get( 2 ).equals( graph.getNode( "b" ) ) )
{
if ( path.get( 6 ).equals( graph.getNode( "d" ) ) )
{
pathBD = true;
}
else if ( path.get( 6 ).equals( graph.getNode( "d2" ) ) )
{
pathBD2 = true;
}
}
else
{
if ( path.get( 6 ).equals( graph.getNode( "d" ) ) )
{
pathB2D = true;
}
else if ( path.get( 6 ).equals( graph.getNode( "d2" ) ) )
{
pathB2D2 = true;
}
}
}
assertTrue( pathBD );
assertTrue( pathB2D );
assertTrue( pathBD2 );
assertTrue( pathB2D2 );
}
@Test
public void test8()
{
Relationship edgeAB = graph.makeEdge( "a", "b" );
Relationship edgeBC = graph.makeEdge( "b", "c" );
Relationship edgeCD = graph.makeEdge( "c", "d" );
Relationship edgeDE = graph.makeEdge( "d", "e" );
Relationship edgeAB2 = graph.makeEdge( "a", "b2" );
Relationship edgeB2C = graph.makeEdge( "b2", "c" );
Relationship edgeCD2 = graph.makeEdge( "c", "d2" );
Relationship edgeD2E = graph.makeEdge( "d2", "e" );
Dijkstra<Double> dijkstra = new Dijkstra<Double>(
0.0,
graph.getNode( "a" ),
graph.getNode( "e" ),
new CostEvaluator<Double>()
{
public Double getCost( Relationship relationship,
Direction direction )
{
return 1.0;
}
}, new DoubleAdder(), new DoubleComparator(), Direction.OUTGOING,
MyRelTypes.R1 );
// path discovery flags
boolean pathBD = false;
boolean pathB2D = false;
boolean pathBD2 = false;
boolean pathB2D2 = false;
List<List<Relationship>> paths = dijkstra.getPathsAsRelationships();
assertTrue( paths.size() == 4 );
for ( List<Relationship> path : paths )
{
assertTrue( path.size() == 4 );
// first choice
if ( path.get( 0 ).equals( edgeAB ) )
{
assertTrue( path.get( 1 ).equals( edgeBC ) );
}
else
{
assertTrue( path.get( 0 ).equals( edgeAB2 ) );
assertTrue( path.get( 1 ).equals( edgeB2C ) );
}
// second choice
if ( path.get( 2 ).equals( edgeCD ) )
{
assertTrue( path.get( 3 ).equals( edgeDE ) );
}
else
{
assertTrue( path.get( 2 ).equals( edgeCD2 ) );
assertTrue( path.get( 3 ).equals( edgeD2E ) );
}
// combinations
if ( path.get( 0 ).equals( edgeAB ) )
{
if ( path.get( 2 ).equals( edgeCD ) )
{
pathBD = true;
}
else if ( path.get( 2 ).equals( edgeCD2 ) )
{
pathBD2 = true;
}
}
else
{
if ( path.get( 2 ).equals( edgeCD ) )
{
pathB2D = true;
}
else if ( path.get( 2 ).equals( edgeCD2 ) )
{
pathB2D2 = true;
}
}
}
assertTrue( pathBD );
assertTrue( pathB2D );
assertTrue( pathBD2 );
assertTrue( pathB2D2 );
}
}