package org.neo4j.graphalgo.path; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.junit.Ignore; import org.junit.Test; import org.neo4j.graphalgo.CommonEvaluators; import org.neo4j.graphalgo.EstimateEvaluator; import org.neo4j.graphalgo.GraphAlgoFactory; import org.neo4j.graphalgo.PathFinder; import org.neo4j.graphalgo.WeightedPath; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Path; import org.neo4j.graphdb.Relationship; import org.neo4j.kernel.Traversal; import common.Neo4jAlgoTestCase; public class TestAStar extends Neo4jAlgoTestCase { static EstimateEvaluator<Double> ESTIMATE_EVALUATOR = new EstimateEvaluator<Double>() { public Double getCost( Node node, Node goal ) { double dx = (Double) node.getProperty( "x" ) - (Double) goal.getProperty( "x" ); double dy = (Double) node.getProperty( "y" ) - (Double) goal.getProperty( "y" ); double result = Math.sqrt( Math.pow( dx, 2 ) + Math.pow( dy, 2 ) ); return result; } }; private PathFinder<WeightedPath> newFinder() { return GraphAlgoFactory.aStar( Traversal.expanderForAllTypes(), CommonEvaluators.doubleCostEvaluator( "length" ), ESTIMATE_EVALUATOR ); } @Test public void testSimplest() { Node nodeA = graph.makeNode( "A", "x", 0d, "y", 0d ); Node nodeB = graph.makeNode( "B", "x", 2d, "y", 1d ); Node nodeC = graph.makeNode( "C", "x", 7d, "y", 0d ); Relationship relAB = graph.makeEdge( "A", "B", "length", 2d ); Relationship relAB2 = graph.makeEdge( "A", "B", "length", 2d ); Relationship relBC = graph.makeEdge( "B", "C", "length", 3d ); Relationship relAC = graph.makeEdge( "A", "C", "length", 10d ); PathFinder<WeightedPath> astar = newFinder(); int counter = 0; for ( WeightedPath path : astar.findAllPaths( nodeA, nodeC ) ) { assertEquals( (Double)5d, (Double)path.weight() ); assertPath( path, nodeA, nodeB, nodeC ); counter++; } // assertEquals( 2, counter ); } @Ignore @Test public void canGetMultiplePathsInTriangleGraph() throws Exception { Node nodeA = graph.makeNode( "A", "x", 0d, "y", 0d ); Node nodeB = graph.makeNode( "B", "x", 2d, "y", 1d ); Node nodeC = graph.makeNode( "C", "x", 7d, "y", 0d ); Set<Relationship> expectedFirsts = new HashSet<Relationship>(); expectedFirsts.add( graph.makeEdge( "A", "B", "length", 1d ) ); expectedFirsts.add( graph.makeEdge( "A", "B", "length", 1d ) ); Relationship expectedSecond = graph.makeEdge( "B", "C", "length", 2d ); graph.makeEdge( "A", "C", "length", 5d ); PathFinder<WeightedPath> algo = newFinder(); Iterator<WeightedPath> paths = algo.findAllPaths( nodeA, nodeC ).iterator(); for ( int i = 0; i < 2; i++ ) { assertTrue( "expected more paths (i=" + i + ")", paths.hasNext() ); Path path = paths.next(); assertPath( path, nodeA, nodeB, nodeC ); Iterator<Relationship> relationships = path.relationships().iterator(); assertTrue( "found shorter path than expected", relationships.hasNext() ); assertTrue( "path contained unexpected relationship", expectedFirsts.remove( relationships.next() ) ); assertTrue( "found shorter path than expected", relationships.hasNext() ); assertEquals( expectedSecond, relationships.next() ); assertFalse( "found longer path than expected", relationships.hasNext() ); } assertFalse( "expected at most two paths", paths.hasNext() ); } @Ignore @Test public void canGetMultiplePathsInASmallRoadNetwork() throws Exception { Node nodeA = graph.makeNode( "A", "x", 1d, "y", 1d ); Node nodeB = graph.makeNode( "B", "x", 2d, "y", 2d ); Node nodeC = graph.makeNode( "C", "x", 0d, "y", 3d ); Node nodeD = graph.makeNode( "D", "x", 1d, "y", 4d ); Node nodeE = graph.makeNode( "E", "x", 1d, "y", 4d ); Node nodeF = graph.makeNode( "F", "x", 1d, "y", 4d ); graph.makeEdge( "A", "B", "length", 2d ); graph.makeEdge( "A", "C", "length", 2.5d ); graph.makeEdge( "C", "D", "length", 7.3d ); graph.makeEdge( "B", "D", "length", 2.5d ); graph.makeEdge( "D", "E", "length", 3d ); graph.makeEdge( "C", "E", "length", 5d ); graph.makeEdge( "E", "F", "length", 5d ); graph.makeEdge( "C", "F", "length", 12d ); graph.makeEdge( "A", "F", "length", 25d ); PathFinder<WeightedPath> algo = newFinder(); // Try the search in both directions. for ( Node[] nodes : new Node[][] { { nodeA, nodeF }, { nodeF, nodeA } } ) { int found = 0; Iterator<WeightedPath> paths = algo.findAllPaths( nodes[0], nodes[1] ).iterator(); for ( int i = 0; i < 2; i++ ) { assertTrue( "expected more paths (i=" + i + ")", paths.hasNext() ); Path path = paths.next(); if ( path.length() != found && path.length() == 3 ) { assertContains( path.nodes(), nodeA, nodeC, nodeE, nodeF ); } else if ( path.length() != found && path.length() == 4 ) { assertContains( path.nodes(), nodeA, nodeB, nodeD, nodeE, nodeF ); } else { fail( "unexpected path length: " + path.length() ); } found = path.length(); } assertFalse( "expected at most two paths", paths.hasNext() ); } } }