gonum.org/v1/gonum@v0.14.0/graph/path/dynamic/dstarlite.go (about) 1 // Copyright ©2014 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package dynamic 6 7 import ( 8 "container/heap" 9 "fmt" 10 "math" 11 12 "gonum.org/v1/gonum/graph" 13 "gonum.org/v1/gonum/graph/path" 14 "gonum.org/v1/gonum/graph/simple" 15 ) 16 17 // DStarLite implements the D* Lite dynamic re-planning path search algorithm. 18 // 19 // doi:10.1109/tro.2004.838026 and ISBN:0-262-51129-0 pp476-483 20 type DStarLite struct { 21 s, t *dStarLiteNode 22 last *dStarLiteNode 23 24 model WorldModel 25 queue dStarLiteQueue 26 keyModifier float64 27 28 weight path.Weighting 29 heuristic path.Heuristic 30 } 31 32 // WorldModel is a mutable weighted directed graph that returns nodes identified 33 // by id number. 34 type WorldModel interface { 35 graph.WeightedBuilder 36 graph.WeightedDirected 37 } 38 39 // NewDStarLite returns a new DStarLite planner for the path from s to t in g using the 40 // heuristic h. The world model, m, is used to store shortest path information during path 41 // planning. The world model must be an empty graph when NewDStarLite is called. 42 // 43 // If h is nil, the DStarLite will use the g.HeuristicCost method if g implements 44 // path.HeuristicCoster, falling back to path.NullHeuristic otherwise. If the graph does not 45 // implement graph.Weighter, path.UniformCost is used. NewDStarLite will panic if g has 46 // a negative edge weight. 47 func NewDStarLite(s, t graph.Node, g graph.Graph, h path.Heuristic, m WorldModel) *DStarLite { 48 /* 49 procedure Initialize() 50 {02”} U = ∅; 51 {03”} k_m = 0; 52 {04”} for all s ∈ S rhs(s) = g(s) = ∞; 53 {05”} rhs(s_goal) = 0; 54 {06”} U.Insert(s_goal, [h(s_start, s_goal); 0]); 55 */ 56 57 d := &DStarLite{ 58 s: newDStarLiteNode(s), 59 t: newDStarLiteNode(t), // badKey is overwritten below. 60 61 model: m, 62 63 heuristic: h, 64 } 65 d.t.rhs = 0 66 67 /* 68 procedure Main() 69 {29”} s_last = s_start; 70 {30”} Initialize(); 71 */ 72 d.last = d.s 73 74 if wg, ok := g.(graph.Weighted); ok { 75 d.weight = wg.Weight 76 } else { 77 d.weight = path.UniformCost(g) 78 } 79 if d.heuristic == nil { 80 if g, ok := g.(path.HeuristicCoster); ok { 81 d.heuristic = g.HeuristicCost 82 } else { 83 d.heuristic = path.NullHeuristic 84 } 85 } 86 87 d.queue.insert(d.t, key{d.heuristic(s, t), 0}) 88 89 nodes := g.Nodes() 90 for nodes.Next() { 91 n := nodes.Node() 92 switch n.ID() { 93 case d.s.ID(): 94 d.model.AddNode(d.s) 95 case d.t.ID(): 96 d.model.AddNode(d.t) 97 default: 98 d.model.AddNode(newDStarLiteNode(n)) 99 } 100 } 101 model := d.model.Nodes() 102 for model.Next() { 103 u := model.Node() 104 uid := u.ID() 105 to := g.From(uid) 106 for to.Next() { 107 v := to.Node() 108 vid := v.ID() 109 w := edgeWeight(d.weight, uid, vid) 110 if w < 0 { 111 panic("D* Lite: negative edge weight") 112 } 113 d.model.SetWeightedEdge(simple.WeightedEdge{F: u, T: d.model.Node(vid), W: w}) 114 } 115 } 116 117 /* 118 procedure Main() 119 {31”} ComputeShortestPath(); 120 */ 121 d.findShortestPath() 122 123 return d 124 } 125 126 // edgeWeight is a helper function that returns the weight of the edge between 127 // two connected nodes, u and v, using the provided weight function. It panics 128 // if there is no edge between u and v. 129 func edgeWeight(weight path.Weighting, uid, vid int64) float64 { 130 w, ok := weight(uid, vid) 131 if !ok { 132 panic("D* Lite: unexpected invalid weight") 133 } 134 return w 135 } 136 137 // keyFor is the CalculateKey procedure in the D* Lite papers. 138 func (d *DStarLite) keyFor(s *dStarLiteNode) key { 139 /* 140 procedure CalculateKey(s) 141 {01”} return [min(g(s), rhs(s)) + h(s_start, s) + k_m; min(g(s), rhs(s))]; 142 */ 143 k := key{1: math.Min(s.g, s.rhs)} 144 k[0] = k[1] + d.heuristic(d.s.Node, s.Node) + d.keyModifier 145 return k 146 } 147 148 // update is the UpdateVertex procedure in the D* Lite papers. 149 func (d *DStarLite) update(u *dStarLiteNode) { 150 /* 151 procedure UpdateVertex(u) 152 {07”} if (g(u) != rhs(u) AND u ∈ U) U.Update(u,CalculateKey(u)); 153 {08”} else if (g(u) != rhs(u) AND u /∈ U) U.Insert(u,CalculateKey(u)); 154 {09”} else if (g(u) = rhs(u) AND u ∈ U) U.Remove(u); 155 */ 156 inQueue := u.inQueue() 157 switch { 158 case inQueue && u.g != u.rhs: 159 d.queue.update(u, d.keyFor(u)) 160 case !inQueue && u.g != u.rhs: 161 d.queue.insert(u, d.keyFor(u)) 162 case inQueue && u.g == u.rhs: 163 d.queue.remove(u) 164 } 165 } 166 167 // findShortestPath is the ComputeShortestPath procedure in the D* Lite papers. 168 func (d *DStarLite) findShortestPath() { 169 /* 170 procedure ComputeShortestPath() 171 {10”} while (U.TopKey() < CalculateKey(s_start) OR rhs(s_start) > g(s_start)) 172 {11”} u = U.Top(); 173 {12”} k_old = U.TopKey(); 174 {13”} k_new = CalculateKey(u); 175 {14”} if(k_old < k_new) 176 {15”} U.Update(u, k_new); 177 {16”} else if (g(u) > rhs(u)) 178 {17”} g(u) = rhs(u); 179 {18”} U.Remove(u); 180 {19”} for all s ∈ Pred(u) 181 {20”} if (s != s_goal) rhs(s) = min(rhs(s), c(s, u) + g(u)); 182 {21”} UpdateVertex(s); 183 {22”} else 184 {23”} g_old = g(u); 185 {24”} g(u) = ∞; 186 {25”} for all s ∈ Pred(u) ∪ {u} 187 {26”} if (rhs(s) = c(s, u) + g_old) 188 {27”} if (s != s_goal) rhs(s) = min s'∈Succ(s)(c(s, s') + g(s')); 189 {28”} UpdateVertex(s); 190 */ 191 for d.queue.Len() != 0 { // We use d.queue.Len since d.queue does not return an infinite key when empty. 192 u := d.queue.top() 193 if !u.key.less(d.keyFor(d.s)) && d.s.rhs <= d.s.g { 194 break 195 } 196 uid := u.ID() 197 switch kNew := d.keyFor(u); { 198 case u.key.less(kNew): 199 d.queue.update(u, kNew) 200 case u.g > u.rhs: 201 u.g = u.rhs 202 d.queue.remove(u) 203 from := d.model.To(uid) 204 for from.Next() { 205 s := from.Node().(*dStarLiteNode) 206 sid := s.ID() 207 if sid != d.t.ID() { 208 s.rhs = math.Min(s.rhs, edgeWeight(d.model.Weight, sid, uid)+u.g) 209 } 210 d.update(s) 211 } 212 default: 213 gOld := u.g 214 u.g = math.Inf(1) 215 for _, _s := range append(graph.NodesOf(d.model.To(uid)), u) { 216 s := _s.(*dStarLiteNode) 217 sid := s.ID() 218 if s.rhs == edgeWeight(d.model.Weight, sid, uid)+gOld { 219 if s.ID() != d.t.ID() { 220 s.rhs = math.Inf(1) 221 to := d.model.From(sid) 222 for to.Next() { 223 t := to.Node() 224 tid := t.ID() 225 s.rhs = math.Min(s.rhs, edgeWeight(d.model.Weight, sid, tid)+t.(*dStarLiteNode).g) 226 } 227 } 228 } 229 d.update(s) 230 } 231 } 232 } 233 } 234 235 // Step performs one movement step along the best path towards the goal. 236 // It returns false if no further progression toward the goal can be 237 // achieved, either because the goal has been reached or because there 238 // is no path. 239 func (d *DStarLite) Step() bool { 240 /* 241 procedure Main() 242 {32”} while (s_start != s_goal) 243 {33”} // if (rhs(s_start) = ∞) then there is no known path 244 {34”} s_start = argmin s'∈Succ(s_start)(c(s_start, s') + g(s')); 245 */ 246 if d.s.ID() == d.t.ID() { 247 return false 248 } 249 if math.IsInf(d.s.rhs, 1) { 250 return false 251 } 252 253 // We use rhs comparison to break ties 254 // between coequally weighted nodes. 255 rhs := math.Inf(1) 256 min := math.Inf(1) 257 258 var next *dStarLiteNode 259 dsid := d.s.ID() 260 to := d.model.From(dsid) 261 for to.Next() { 262 s := to.Node().(*dStarLiteNode) 263 w := edgeWeight(d.model.Weight, dsid, s.ID()) + s.g 264 if w < min || (w == min && s.rhs < rhs) { 265 next = s 266 min = w 267 rhs = s.rhs 268 } 269 } 270 d.s = next 271 272 /* 273 procedure Main() 274 {35”} Move to s_start; 275 */ 276 return true 277 } 278 279 // MoveTo moves to n in the world graph. 280 func (d *DStarLite) MoveTo(n graph.Node) { 281 d.last = d.s 282 d.s = d.model.Node(n.ID()).(*dStarLiteNode) 283 d.keyModifier += d.heuristic(d.last, d.s) 284 } 285 286 // UpdateWorld updates or adds edges in the world graph. UpdateWorld will 287 // panic if changes include a negative edge weight. 288 func (d *DStarLite) UpdateWorld(changes []graph.Edge) { 289 /* 290 procedure Main() 291 {36”} Scan graph for changed edge costs; 292 {37”} if any edge costs changed 293 {38”} k_m = k_m + h(s_last, s_start); 294 {39”} s_last = s_start; 295 {40”} for all directed edges (u, v) with changed edge costs 296 {41”} c_old = c(u, v); 297 {42”} Update the edge cost c(u, v); 298 {43”} if (c_old > c(u, v)) 299 {44”} if (u != s_goal) rhs(u) = min(rhs(u), c(u, v) + g(v)); 300 {45”} else if (rhs(u) = c_old + g(v)) 301 {46”} if (u != s_goal) rhs(u) = min s'∈Succ(u)(c(u, s') + g(s')); 302 {47”} UpdateVertex(u); 303 {48”} ComputeShortestPath() 304 */ 305 if len(changes) == 0 { 306 return 307 } 308 d.keyModifier += d.heuristic(d.last, d.s) 309 d.last = d.s 310 for _, e := range changes { 311 from := e.From() 312 fid := from.ID() 313 to := e.To() 314 tid := to.ID() 315 c, _ := d.weight(fid, tid) 316 if c < 0 { 317 panic("D* Lite: negative edge weight") 318 } 319 cOld, _ := d.model.Weight(fid, tid) 320 u := d.worldNodeFor(from) 321 v := d.worldNodeFor(to) 322 d.model.SetWeightedEdge(simple.WeightedEdge{F: u, T: v, W: c}) 323 uid := u.ID() 324 if cOld > c { 325 if uid != d.t.ID() { 326 u.rhs = math.Min(u.rhs, c+v.g) 327 } 328 } else if u.rhs == cOld+v.g { 329 if uid != d.t.ID() { 330 u.rhs = math.Inf(1) 331 to := d.model.From(uid) 332 for to.Next() { 333 t := to.Node() 334 u.rhs = math.Min(u.rhs, edgeWeight(d.model.Weight, uid, t.ID())+t.(*dStarLiteNode).g) 335 } 336 } 337 } 338 d.update(u) 339 } 340 d.findShortestPath() 341 } 342 343 func (d *DStarLite) worldNodeFor(n graph.Node) *dStarLiteNode { 344 switch w := d.model.Node(n.ID()).(type) { 345 case *dStarLiteNode: 346 return w 347 case graph.Node: 348 panic(fmt.Sprintf("D* Lite: illegal world model node type: %T", w)) 349 default: 350 return newDStarLiteNode(n) 351 } 352 } 353 354 // Here returns the current location. 355 func (d *DStarLite) Here() graph.Node { 356 return d.s.Node 357 } 358 359 // Path returns the path from the current location to the goal and the 360 // weight of the path. 361 func (d *DStarLite) Path() (p []graph.Node, weight float64) { 362 u := d.s 363 p = []graph.Node{u.Node} 364 for u.ID() != d.t.ID() { 365 if math.IsInf(u.rhs, 1) { 366 return nil, math.Inf(1) 367 } 368 369 // We use stored rhs comparison to break 370 // ties between calculated rhs-coequal nodes. 371 rhsMin := math.Inf(1) 372 min := math.Inf(1) 373 var ( 374 next *dStarLiteNode 375 cost float64 376 ) 377 uid := u.ID() 378 to := d.model.From(uid) 379 for to.Next() { 380 v := to.Node().(*dStarLiteNode) 381 vid := v.ID() 382 w := edgeWeight(d.model.Weight, uid, vid) 383 if rhs := w + v.g; rhs < min || (rhs == min && v.rhs < rhsMin) { 384 next = v 385 min = rhs 386 rhsMin = v.rhs 387 cost = w 388 } 389 } 390 if next == nil { 391 return nil, math.NaN() 392 } 393 u = next 394 weight += cost 395 p = append(p, u.Node) 396 } 397 return p, weight 398 } 399 400 /* 401 The pseudocode uses the following functions to manage the priority 402 queue: 403 404 * U.Top() returns a vertex with the smallest priority of all 405 vertices in priority queue U. 406 * U.TopKey() returns the smallest priority of all vertices in 407 priority queue U. (If is empty, then U.TopKey() returns [∞;∞].) 408 * U.Pop() deletes the vertex with the smallest priority in 409 priority queue U and returns the vertex. 410 * U.Insert(s, k) inserts vertex s into priority queue with 411 priority k. 412 * U.Update(s, k) changes the priority of vertex s in priority 413 queue U to k. (It does nothing if the current priority of vertex 414 s already equals k.) 415 * Finally, U.Remove(s) removes vertex s from priority queue U. 416 */ 417 418 // key is a D* Lite priority queue key. 419 type key [2]float64 420 421 // badKey is a poisoned key. Testing for a bad key uses NaN inequality. 422 var badKey = key{math.NaN(), math.NaN()} 423 424 func (k key) isBadKey() bool { return k != k } 425 426 // less returns whether k is less than other. From ISBN:0-262-51129-0 pp476-483: 427 // 428 // k ≤ k' iff k₁ < k'₁ OR (k₁ == k'₁ AND k₂ ≤ k'₂) 429 func (k key) less(other key) bool { 430 if k.isBadKey() || other.isBadKey() { 431 panic("D* Lite: poisoned key") 432 } 433 return k[0] < other[0] || (k[0] == other[0] && k[1] < other[1]) 434 } 435 436 // dStarLiteNode adds D* Lite accounting to a graph.Node. 437 type dStarLiteNode struct { 438 graph.Node 439 key key 440 idx int 441 rhs float64 442 g float64 443 } 444 445 // newDStarLiteNode returns a dStarLite node that is in a legal state 446 // for existence outside the DStarLite priority queue. 447 func newDStarLiteNode(n graph.Node) *dStarLiteNode { 448 return &dStarLiteNode{ 449 Node: n, 450 rhs: math.Inf(1), 451 g: math.Inf(1), 452 key: badKey, 453 idx: -1, 454 } 455 } 456 457 // inQueue returns whether the node is in the queue. 458 func (q *dStarLiteNode) inQueue() bool { 459 return q.idx >= 0 460 } 461 462 // dStarLiteQueue is a D* Lite priority queue. 463 type dStarLiteQueue []*dStarLiteNode 464 465 func (q dStarLiteQueue) Less(i, j int) bool { 466 return q[i].key.less(q[j].key) 467 } 468 469 func (q dStarLiteQueue) Swap(i, j int) { 470 q[i], q[j] = q[j], q[i] 471 q[i].idx = i 472 q[j].idx = j 473 } 474 475 func (q dStarLiteQueue) Len() int { 476 return len(q) 477 } 478 479 func (q *dStarLiteQueue) Push(x interface{}) { 480 n := x.(*dStarLiteNode) 481 n.idx = len(*q) 482 *q = append(*q, n) 483 } 484 485 func (q *dStarLiteQueue) Pop() interface{} { 486 n := (*q)[len(*q)-1] 487 n.idx = -1 488 *q = (*q)[:len(*q)-1] 489 return n 490 } 491 492 // top returns the top node in the queue. Note that instead of 493 // returning a key [∞;∞] when q is empty, the caller checks for 494 // an empty queue by calling q.Len. 495 func (q dStarLiteQueue) top() *dStarLiteNode { 496 return q[0] 497 } 498 499 // insert puts the node u into the queue with the key k. 500 func (q *dStarLiteQueue) insert(u *dStarLiteNode, k key) { 501 u.key = k 502 heap.Push(q, u) 503 } 504 505 // update updates the node in the queue identified by id with the key k. 506 func (q *dStarLiteQueue) update(n *dStarLiteNode, k key) { 507 n.key = k 508 heap.Fix(q, n.idx) 509 } 510 511 // remove removes the node identified by id from the queue. 512 func (q *dStarLiteQueue) remove(n *dStarLiteNode) { 513 heap.Remove(q, n.idx) 514 n.key = badKey 515 n.idx = -1 516 }