Added proper oriented search

This commit is contained in:
Andriy Kashcha 2017-09-18 01:35:16 -07:00
parent 29ce133e59
commit 889a5d28a5
4 changed files with 151 additions and 12 deletions

View File

@ -61,6 +61,8 @@ function aStarBi(graph, options) {
pool.reset();
var callVisitor = oriented ? orientedVisitor : nonOrientedVisitor;
// Maps nodeId to NodeSearchState.
var nodeState = new Map();
@ -116,19 +118,32 @@ function aStarBi(graph, options) {
if (current.distanceToSource > lMin) continue;
graph.forEachLinkedNode(current.node.id, callVisitor, oriented);
graph.forEachLinkedNode(current.node.id, callVisitor);
if (minFrom && minTo) {
// This is not necessary the best path, but we are so greedy that we
// can't resist:
return reconstructBiDirectionalPath(minFrom, minTo);
}
}
return NO_PATH; // No path.
function callVisitor(otherNode, link) {
function nonOrientedVisitor(otherNode, link) {
return visitNode(otherNode, link, current);
}
function orientedVisitor(otherNode, link) {
// For oritned graphs we need to reverse graph, when traveling
// backwards. So, we use non-oriented ngraph's traversal, and
// filter link orientation here.
if (currentOpener === BY_FROM) {
if (link.fromId === current.node.id) return visitNode(otherNode, link, current)
} else if (currentOpener === BY_TO) {
if (link.toId === current.node.id) return visitNode(otherNode, link, current);
}
}
function canExit(currentNode) {
var opener = currentNode.open
if (opener && opener !== currentOpener) {
@ -142,12 +157,12 @@ function aStarBi(graph, options) {
var pathOfNodes = [];
var aParent = a;
while(aParent) {
pathOfNodes.unshift(aParent.node);
pathOfNodes.push(aParent.node);
aParent = aParent.parent;
}
var bParent = b;
while (bParent) {
pathOfNodes.push(bParent.node);
pathOfNodes.unshift(bParent.node);
bParent = bParent.parent
}
return pathOfNodes;
@ -180,7 +195,6 @@ function aStarBi(graph, options) {
return;
}
var tentativeDistance = cameFrom.distanceToSource + distance(otherSearchState.node, cameFrom.node, link);
if (tentativeDistance >= otherSearchState.distanceToSource) {

View File

@ -38,6 +38,9 @@ function nba(graph, options) {
pool.reset();
var forwardVisitor = oriented ? visitN1Oriented : visitN1;
var reverseVisitor = oriented ? visitN2Oriented : visitN2;
// Maps nodeId to NBASearchState.
var nodeState = new Map();
@ -90,7 +93,7 @@ function nba(graph, options) {
cameFrom.closed = true;
if (cameFrom.f1 < lMin && (cameFrom.g1 + f2 - heuristic(from, cameFrom.node)) < lMin) {
graph.forEachLinkedNode(cameFrom.node.id, visitN1, options.oriented) // todo - needs to reverse correctly
graph.forEachLinkedNode(cameFrom.node.id, forwardVisitor);
}
if (open1Set.length > 0) {
@ -106,7 +109,7 @@ function nba(graph, options) {
cameFrom.closed = true;
if (cameFrom.f2 < lMin && (cameFrom.g2 + f1 - heuristic(cameFrom.node, to)) < lMin) {
graph.forEachLinkedNode(cameFrom.node.id, visitN2, options.oriented) // todo - needs to reverse correctly
graph.forEachLinkedNode(cameFrom.node.id, reverseVisitor);
}
if (open2Set.length > 0) {
@ -169,6 +172,15 @@ function nba(graph, options) {
minNode = otherSearchState;
}
}
function visitN2Oriented(otherNode, link) {
// we are going backwards, graph needs to be reversed.
if (link.toId === cameFrom.node.id) return visitN2(otherNode, link);
}
function visitN1Oriented(otherNode, link) {
// this is forward direction, so we should be coming FROM:
if (link.fromId === cameFrom.node.id) return visitN1(otherNode, link);
}
}
}

View File

@ -6,7 +6,6 @@ var fromDot = require('ngraph.fromdot');
var asciiUtils = require('./utils/graphFromAscii');
test('it can find weighted', t => {
let createGraph = require('ngraph.graph');
let graph = createGraph();
graph.addLink('a', 'b', {weight: 10});
@ -28,6 +27,64 @@ test('it can find weighted', t => {
t.end();
});
test('A* can find directed path', t => {
let graph = createGraph();
// We want to find a path from a to e.
// a -> b <- e
// \ /
// c -> d
// In undirected graph the `a, b, e` will be the solution.
// In directed graph it sohuld be `a c d e`
graph.addLink('a', 'b');
graph.addLink('e', 'b');
graph.addLink('a', 'c');
graph.addLink('c', 'd');
graph.addLink('d', 'e');
var pathFinder = aStar(graph, {
oriented: true
});
let path = pathFinder.find('a', 'e');
t.equals(path[0].id, 'e', 'e is here');
t.equals(path[1].id, 'd', 'd is here');
t.equals(path[2].id, 'c', 'c is here');
t.equals(path[3].id, 'a', 'a is here');
t.end();
});
test('A* greedy can find directed path', t => {
let graph = createGraph();
// We want to find a path from a to e.
// a -> b <- e
// \ /
// c -> d
// In undirected graph the `a, b, e` will be the solution.
// In directed graph it sohuld be `a c d e`
graph.addLink('a', 'b');
graph.addLink('e', 'b');
graph.addLink('a', 'c');
graph.addLink('c', 'd');
graph.addLink('d', 'e');
var pathFinder = aGreedy(graph, {
oriented: true
});
let path = pathFinder.find('a', 'e');
t.equals(path[0].id, 'e', 'e is here');
t.equals(path[1].id, 'd', 'd is here');
t.equals(path[2].id, 'c', 'c is here');
t.equals(path[3].id, 'a', 'a is here');
t.end();
});
test('it can use heuristic', t => {
let createGraph = require('ngraph.graph');
let graph = createGraph();
@ -85,10 +142,12 @@ test('it can find path without any config', t => {
t.equals(path[2].id, 'a', 'a is here');
var pathFinderBi = aGreedy(graph);
var pathBi = pathFinderBi.find('a', 'c');
t.equals(pathBi[0].id, 'c', 'c is here');
t.equals(pathBi[1].id, 'b', 'b is here');
t.equals(pathBi[2].id, 'a', 'a is here');
let foundNodes = new Set();
pathFinderBi.find('a', 'c').forEach(n => foundNodes.add(n.id));
t.ok(foundNodes.has('c'), 'c is here');
t.ok(foundNodes.has('b'), 'b is here');
t.ok(foundNodes.has('a'), 'a is here');
t.end();
})

54
test/nba.js Normal file
View File

@ -0,0 +1,54 @@
var test = require('tap').test;
var nba = require('../').nba;
var createGraph = require('ngraph.graph');
test('it can find path', t => {
let graph = createGraph();
graph.addLink('a', 'b', {weight: 10});
graph.addLink('a', 'c', {weight: 10});
graph.addLink('c', 'd', {weight: 5});
graph.addLink('b', 'd', {weight: 10});
var pathFinder = nba(graph, {
distance(a, b, link) {
return link.data.weight;
}
});
let path = pathFinder.find('a', 'd');
t.equals(path[0].id, 'd', 'd is here');
t.equals(path[1].id, 'c', 'c is here');
t.equals(path[2].id, 'a', 'a is here');
t.end();
});
test('it can find directed path', t => {
let graph = createGraph();
// We want to find a path from a to e.
// a -> b <- e
// \ /
// c -> d
// In undirected graph the `a, b, e` will be the solution.
// In directed graph it sohuld be `a c d e`
graph.addLink('a', 'b');
graph.addLink('e', 'b');
graph.addLink('a', 'c');
graph.addLink('c', 'd');
graph.addLink('d', 'e');
var pathFinder = nba(graph, {
oriented: true
});
let path = pathFinder.find('a', 'e');
t.equals(path[0].id, 'e', 'e is here');
t.equals(path[1].id, 'd', 'd is here');
t.equals(path[2].id, 'c', 'c is here');
t.equals(path[3].id, 'a', 'a is here');
t.end();
});