Make sure linkgraph related stuff is mostly identifcal with JGR Patch
[openttd-joker.git] / src / linkgraph / mcf.cpp
blob9f7b0a41c0210bb5f398e960eb3aaaadb83fb689
1 /** @file mcf.cpp Definition of Multi-Commodity-Flow solver. */
3 #include "../stdafx.h"
4 #include "../core/math_func.hpp"
5 #include "mcf.h"
6 #include "../3rdparty/cpp-btree/btree_map.h"
7 #include <set>
9 #include "../safeguards.h"
11 typedef btree::btree_map<NodeID, Path *> PathViaMap;
13 /**
14 * This is a wrapper around Tannotation* which also stores a cache of GetAnnotation() and GetNode()
15 * to remove the need dereference the Tannotation* pointer when sorting/inseting/erasing in MultiCommodityFlow::Dijkstra::AnnoSet
17 template<typename Tannotation>
18 class AnnoSetItem {
19 public:
20 Tannotation * anno_ptr;
21 typename Tannotation::AnnotationValueType cached_annotation;
22 NodeID node_id;
24 AnnoSetItem(Tannotation *anno) : anno_ptr(anno), cached_annotation(anno->GetAnnotation()), node_id(anno->GetNode()) {}
27 /**
28 * Distance-based annotation for use in the Dijkstra algorithm. This is close
29 * to the original meaning of "annotation" in this context. Paths are rated
30 * according to the sum of distances of their edges.
32 class DistanceAnnotation : public Path {
33 public:
34 typedef uint AnnotationValueType;
36 /**
37 * Constructor.
38 * @param n ID of node to be annotated.
39 * @param source If the node is the source of its path.
41 DistanceAnnotation(NodeID n, bool source = false) : Path(n, source) {}
43 bool IsBetter(const DistanceAnnotation *base, uint cap, int free_cap, uint dist) const;
45 /**
46 * Return the actual value of the annotation, in this case the distance.
47 * @return Distance.
49 inline uint GetAnnotation() const { return this->distance; }
51 /**
52 * Update the cached annotation value
54 inline void UpdateAnnotation() { }
56 /**
57 * Comparator for std containers.
59 struct Comparator {
60 bool operator()(const AnnoSetItem<DistanceAnnotation> &x, const AnnoSetItem<DistanceAnnotation> &y) const;
64 /**
65 * Capacity-based annotation for use in the Dijkstra algorithm. This annotation
66 * rates paths according to the maximum capacity of their edges. The Dijkstra
67 * algorithm still gives meaningful results like this as the capacity of a path
68 * can only decrease or stay the same if you add more edges.
70 class CapacityAnnotation : public Path {
71 int cached_annotation;
73 public:
74 typedef int AnnotationValueType;
76 /**
77 * Constructor.
78 * @param n ID of node to be annotated.
79 * @param source If the node is the source of its path.
81 CapacityAnnotation(NodeID n, bool source = false) : Path(n, source) {}
83 bool IsBetter(const CapacityAnnotation *base, uint cap, int free_cap, uint dist) const;
85 /**
86 * Return the actual value of the annotation, in this case the capacity.
87 * @return Capacity.
89 inline int GetAnnotation() const { return this->cached_annotation; }
91 /**
92 * Update the cached annotation value
94 inline void UpdateAnnotation()
96 this->cached_annotation = this->GetCapacityRatio();
99 /**
100 * Comparator for std containers.
102 struct Comparator {
103 bool operator()(const AnnoSetItem<CapacityAnnotation> &x, const AnnoSetItem<CapacityAnnotation> &y) const;
108 * Iterator class for getting the edges in the order of their next_edge
109 * members.
111 class GraphEdgeIterator {
112 private:
113 LinkGraphJob & job; ///< Job being executed
114 EdgeIterator i; ///< Iterator pointing to current edge.
115 EdgeIterator end; ///< Iterator pointing beyond last edge.
117 public:
120 * Construct a GraphEdgeIterator.
121 * @param job Job to iterate on.
123 GraphEdgeIterator(LinkGraphJob &job) : job(job),
124 i(NULL, NULL, INVALID_NODE), end(NULL, NULL, INVALID_NODE)
128 * Setup the node to start iterating at.
129 * @param source Unused.
130 * @param node Node to start iterating at.
132 void SetNode(NodeID source, NodeID node)
134 this->i = this->job[node].Begin();
135 this->end = this->job[node].End();
139 * Retrieve the ID of the node the next edge points to.
140 * @return Next edge's target node ID or INVALID_NODE.
142 NodeID Next()
144 return this->i != this->end ? (this->i++)->first : INVALID_NODE;
149 * Iterator class for getting edges from a FlowStatMap.
151 class FlowEdgeIterator {
152 private:
153 LinkGraphJob & job; ///< Link graph job we're working with.
155 /** Lookup table for getting NodeIDs from StationIDs. */
156 std::vector<NodeID> station_to_node;
158 /** Current iterator in the shares map. */
159 FlowStat::SharesMap::const_iterator it;
161 /** End of the shares map. */
162 FlowStat::SharesMap::const_iterator end;
163 public:
166 * Constructor.
167 * @param job Link graph job to work with.
169 FlowEdgeIterator(LinkGraphJob &job) : job(job)
171 for (NodeID i = 0; i < job.Size(); ++i) {
172 StationID st = job[i].Station();
173 if (st >= this->station_to_node.size()) {
174 this->station_to_node.resize(st + 1);
176 this->station_to_node[st] = i;
181 * Setup the node to retrieve edges from.
182 * @param source Root of the current path tree.
183 * @param node Current node to be checked for outgoing flows.
185 void SetNode(NodeID source, NodeID node)
187 const FlowStatMap &flows = this->job[node].Flows();
188 FlowStatMap::const_iterator it = flows.find(this->job[source].Station());
189 if (it != flows.end()) {
190 this->it = it->second.GetShares()->begin();
191 this->end = it->second.GetShares()->end();
193 else {
194 this->it = FlowStat::empty_sharesmap.begin();
195 this->end = FlowStat::empty_sharesmap.end();
200 * Get the next node for which a flow exists.
201 * @return ID of next node with flow.
203 NodeID Next()
205 if (this->it == this->end) return INVALID_NODE;
206 return this->station_to_node[(this->it++)->second];
211 * Determines if an extension to the given Path with the given parameters is
212 * better than this path.
213 * @param base Other path.
214 * @param cap Capacity of the new edge to be added to base.
215 * @param dist Distance of the new edge.
216 * @return True if base + the new edge would be better than the path associated
217 * with this annotation.
219 bool DistanceAnnotation::IsBetter(const DistanceAnnotation *base, uint cap,
220 int free_cap, uint dist) const
222 /* If any of the paths is disconnected, the other one is better. If both
223 * are disconnected, this path is better.*/
224 if (base->distance == UINT_MAX) {
225 return false;
227 else if (this->distance == UINT_MAX) {
228 return true;
231 if (free_cap > 0 && base->free_capacity > 0) {
232 /* If both paths have capacity left, compare their distances.
233 * If the other path has capacity left and this one hasn't, the
234 * other one's better (thus, return true). */
235 return this->free_capacity > 0 ? (base->distance + dist < this->distance) : true;
237 else {
238 /* If the other path doesn't have capacity left, but this one has,
239 * the other one is worse (thus, return false).
240 * If both paths are out of capacity, do the regular distance
241 * comparison. */
242 return this->free_capacity > 0 ? false : (base->distance + dist < this->distance);
247 * Determines if an extension to the given Path with the given parameters is
248 * better than this path.
249 * @param base Other path.
250 * @param cap Capacity of the new edge to be added to base.
251 * @param dist Distance of the new edge.
252 * @return True if base + the new edge would be better than the path associated
253 * with this annotation.
255 bool CapacityAnnotation::IsBetter(const CapacityAnnotation *base, uint cap,
256 int free_cap, uint dist) const
258 int min_cap = Path::GetCapacityRatio(min(base->free_capacity, free_cap), min(base->capacity, cap));
259 int this_cap = this->GetAnnotation();
260 if (min_cap == this_cap) {
261 /* If the capacities are the same and the other path isn't disconnected
262 * choose the shorter path. */
263 return base->distance == UINT_MAX ? false : (base->distance + dist < this->distance);
265 else {
266 return min_cap > this_cap;
270 #ifdef CUSTOM_ALLOCATOR
272 * Storage for AnnoSetAllocator instances
274 struct AnnoSetAllocatorStore {
275 std::vector<void *> used_blocks;
276 void *current_block;
277 size_t next_position;
278 void *last_freed;
280 AnnoSetAllocatorStore() : current_block(NULL), next_position(0), last_freed(NULL) {}
282 ~AnnoSetAllocatorStore()
284 for (std::vector<void *>::iterator i = used_blocks.begin(); i != used_blocks.end(); ++i) {
285 free(*i);
291 * Custom allocator specifically for use with MultiCommodityFlow::Dijkstra::AnnoSet
292 * This allocates RB-set nodes in contiguous blocks, and frees all allocated nodes when destructed
293 * If a node is deallocated, it is returned in the next allocation, this is so that the same node
294 * can be re-used across a call to Path::Fork
296 template<class Ttype>
297 struct AnnoSetAllocator {
298 static const size_t block_size = 1024;
300 AnnoSetAllocatorStore &store;
302 void NewBlock()
304 store.current_block = MallocT<Ttype>(block_size);
305 store.next_position = 0;
306 store.used_blocks.push_back(store.current_block);
309 typedef Ttype value_type;
311 template<typename Tother>
312 struct rebind {
313 typedef AnnoSetAllocator<Tother> other;
316 AnnoSetAllocator(AnnoSetAllocatorStore &store) : store(store) {}
318 template<typename Tother>
319 AnnoSetAllocator(const AnnoSetAllocator<Tother> &other) : store(other.store) {}
321 Ttype* allocate(size_t n)
323 if (store.current_block == NULL) NewBlock();
325 assert(n == 1);
326 if (store.last_freed != NULL) {
327 Ttype* out = static_cast<Ttype*>(store.last_freed);
328 store.last_freed = NULL;
329 return out;
332 if (store.next_position == block_size) NewBlock();
334 Ttype* next = static_cast<Ttype*>(store.current_block) + store.next_position;
335 store.next_position++;
336 return next;
339 void deallocate(Ttype* p, size_t n)
341 store.last_freed = p;
344 #endif
347 * Annotation wrapper class which also stores an iterator to the AnnoSet node which points to this annotation
348 * This is to enable erasing the AnnoSet node when calling Path::Fork without having to search the set
350 template<class Tannotation>
351 struct AnnosWrapper : public Tannotation {
352 #ifdef CUSTOM_ALLOCATOR
353 typename std::set<AnnoSetItem<Tannotation>, typename Tannotation::Comparator, AnnoSetAllocator<AnnoSetItem<Tannotation> > >::iterator self_iter;
354 #else
355 typename std::set<AnnoSetItem<Tannotation>, typename Tannotation::Comparator>::iterator self_iter;
356 #endif
359 AnnosWrapper(NodeID n, bool source = false) : Tannotation(n, source) {}
363 * A slightly modified Dijkstra algorithm. Grades the paths not necessarily by
364 * distance, but by the value Tannotation computes. It uses the max_saturation
365 * setting to artificially decrease capacities.
366 * @tparam Tannotation Annotation to be used.
367 * @tparam Tedge_iterator Iterator to be used for getting outgoing edges.
368 * @param source_node Node where the algorithm starts.
369 * @param paths Container for the paths to be calculated.
371 template<class Tannotation, class Tedge_iterator>
372 void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths)
374 #ifdef CUSTOM_ALLOCATOR
375 typedef std::set<AnnoSetItem<Tannotation>, typename Tannotation::Comparator, AnnoSetAllocator<AnnoSetItem<Tannotation> > > AnnoSet;
376 AnnoSetAllocatorStore annos_store;
377 AnnoSet annos = AnnoSet(typename Tannotation::Comparator(), AnnoSetAllocator<Tannotation *>(annos_store));
378 #else
379 typedef std::set<AnnoSetItem<Tannotation>, typename Tannotation::Comparator> AnnoSet;
380 AnnoSet annos = AnnoSet(typename Tannotation::Comparator());
381 #endif
382 Tedge_iterator iter(this->job);
383 uint size = this->job.Size();
384 paths.resize(size, NULL);
386 this->job.path_allocator.SetParameters(sizeof(AnnosWrapper<Tannotation>), (8192 - 32) / sizeof(AnnosWrapper<Tannotation>));
388 for (NodeID node = 0; node < size; ++node) {
389 AnnosWrapper<Tannotation> *anno = new (this->job.path_allocator.Allocate()) AnnosWrapper<Tannotation>(node, node == source_node);
390 anno->UpdateAnnotation();
391 anno->self_iter = (node == source_node) ? annos.insert(AnnoSetItem<Tannotation>(anno)).first : annos.end(); // only insert the source node, the other nodes will be added as reached
392 paths[node] = anno;
394 while (!annos.empty()) {
395 typename AnnoSet::iterator i = annos.begin();
396 AnnosWrapper<Tannotation> *source = static_cast<AnnosWrapper<Tannotation> *>(i->anno_ptr);
397 annos.erase(i);
398 source->self_iter = annos.end();
399 NodeID from = source->GetNode();
400 iter.SetNode(source_node, from);
401 for (NodeID to = iter.Next(); to != INVALID_NODE; to = iter.Next()) {
402 if (to == from) continue; // Not a real edge but a consumption sign.
403 Edge edge = this->job[from][to];
404 uint capacity = edge.Capacity();
405 if (this->max_saturation != UINT_MAX) {
406 capacity *= this->max_saturation;
407 capacity /= 100;
408 if (capacity == 0) capacity = 1;
410 /* punish in-between stops a little */
411 uint distance = DistanceMaxPlusManhattan(this->job[from].XY(), this->job[to].XY()) + 1;
412 AnnosWrapper<Tannotation> *dest = static_cast<AnnosWrapper<Tannotation> *>(paths[to]);
413 if (dest->IsBetter(source, capacity, capacity - edge.Flow(), distance)) {
414 if (dest->self_iter != annos.end()) annos.erase(dest->self_iter);
415 dest->Fork(source, capacity, capacity - edge.Flow(), distance);
416 dest->UpdateAnnotation();
417 dest->self_iter = annos.insert(AnnoSetItem<Tannotation>(dest)).first;
424 * Clean up paths that lead nowhere and the root path.
425 * @param source_id ID of the root node.
426 * @param paths Paths to be cleaned up.
428 void MultiCommodityFlow::CleanupPaths(NodeID source_id, PathVector &paths)
430 Path *source = paths[source_id];
431 paths[source_id] = NULL;
432 for (PathVector::iterator i = paths.begin(); i != paths.end(); ++i) {
433 Path *path = *i;
434 if (path == NULL) continue;
435 if (path->GetParent() == source) path->Detach();
436 while (path != source && path != NULL && path->GetFlow() == 0) {
437 Path *parent = path->GetParent();
438 path->Detach();
439 if (path->GetNumChildren() == 0) {
440 paths[path->GetNode()] = NULL;
441 this->job.path_allocator.Free(path);
443 path = parent;
446 this->job.path_allocator.Free(source);
447 paths.clear();
451 * Push flow along a path and update the unsatisfied_demand of the associated
452 * edge.
453 * @param edge Edge whose ends the path connects.
454 * @param path End of the path the flow should be pushed on.
455 * @param accuracy Accuracy of the calculation.
456 * @param max_saturation If < UINT_MAX only push flow up to the given
457 * saturation, otherwise the path can be "overloaded".
459 uint MultiCommodityFlow::PushFlow(Edge &edge, Path *path, uint accuracy,
460 uint max_saturation)
462 assert(edge.UnsatisfiedDemand() > 0);
463 uint flow = Clamp(edge.Demand() / accuracy, 1, edge.UnsatisfiedDemand());
464 flow = path->AddFlow(flow, this->job, max_saturation);
465 edge.SatisfyDemand(flow);
466 return flow;
470 * Find the flow along a cycle including cycle_begin in path.
471 * @param path Set of paths that form the cycle.
472 * @param cycle_begin Path to start at.
473 * @return Flow along the cycle.
475 uint MCF1stPass::FindCycleFlow(const PathVector &path, const Path *cycle_begin)
477 uint flow = UINT_MAX;
478 const Path *cycle_end = cycle_begin;
479 do {
480 flow = min(flow, cycle_begin->GetFlow());
481 cycle_begin = path[cycle_begin->GetNode()];
482 } while (cycle_begin != cycle_end);
483 return flow;
487 * Eliminate a cycle of the given flow in the given set of paths.
488 * @param path Set of paths containing the cycle.
489 * @param cycle_begin Part of the cycle to start at.
490 * @param flow Flow along the cycle.
492 void MCF1stPass::EliminateCycle(PathVector &path, Path *cycle_begin, uint flow)
494 Path *cycle_end = cycle_begin;
495 do {
496 NodeID prev = cycle_begin->GetNode();
497 cycle_begin->ReduceFlow(flow);
498 if (cycle_begin->GetFlow() == 0) {
499 PathList &node_paths = this->job[cycle_begin->GetParent()->GetNode()].Paths();
500 for (PathList::reverse_iterator i = node_paths.rbegin(); i != node_paths.rend(); ++i) {
501 if (*i == cycle_begin) {
502 *i = nullptr;
503 break;
507 cycle_begin = path[prev];
508 Edge edge = this->job[prev][cycle_begin->GetNode()];
509 edge.RemoveFlow(flow);
510 } while (cycle_begin != cycle_end);
514 * Eliminate cycles for origin_id in the graph. Start searching at next_id and
515 * work recursively. Also "summarize" paths: Add up the flows along parallel
516 * paths in one.
517 * @param path Paths checked in parent calls to this method.
518 * @param origin_id Origin of the paths to be checked.
519 * @param next_id Next node to be checked.
520 * @return If any cycles have been found and eliminated.
522 bool MCF1stPass::EliminateCycles(PathVector &path, NodeID origin_id, NodeID next_id)
524 Path *at_next_pos = path[next_id];
526 /* this node has already been searched */
527 if (at_next_pos == Path::invalid_path) return false;
529 if (at_next_pos == NULL) {
530 /* Summarize paths; add up the paths with the same source and next hop
531 * in one path each. */
532 PathList &paths = this->job[next_id].Paths();
533 PathViaMap next_hops;
534 uint holes = 0;
535 for (PathList::reverse_iterator i = paths.rbegin(); i != paths.rend();) {
536 Path *new_child = *i;
537 if (new_child) {
538 uint new_flow = new_child->GetFlow();
539 if (new_flow == 0) break;
540 if (new_child->GetOrigin() == origin_id) {
541 PathViaMap::iterator via_it = next_hops.find(new_child->GetNode());
542 if (via_it == next_hops.end()) {
543 next_hops[new_child->GetNode()] = new_child;
545 else {
546 Path *child = via_it->second;
547 child->AddFlow(new_flow);
548 new_child->ReduceFlow(new_flow);
550 *i = nullptr;
551 holes++;
555 else {
556 holes++;
558 ++i;
560 if (holes >= paths.size() / 8) {
561 /* remove any holes */
562 paths.erase(std::remove(paths.begin(), paths.end(), nullptr), paths.end());
565 bool found = false;
566 /* Search the next hops for nodes we have already visited */
567 for (PathViaMap::iterator via_it = next_hops.begin();
568 via_it != next_hops.end(); ++via_it) {
569 Path *child = via_it->second;
570 if (child->GetFlow() > 0) {
571 /* Push one child into the path vector and search this child's
572 * children. */
573 path[next_id] = child;
574 found = this->EliminateCycles(path, origin_id, child->GetNode()) || found;
577 /* All paths departing from this node have been searched. Mark as
578 * resolved if no cycles found. If cycles were found further cycles
579 * could be found in this branch, thus it has to be searched again next
580 * time we spot it.
582 path[next_id] = found ? NULL : Path::invalid_path;
583 return found;
586 /* This node has already been visited => we have a cycle.
587 * Backtrack to find the exact flow. */
588 uint flow = this->FindCycleFlow(path, at_next_pos);
589 if (flow > 0) {
590 this->EliminateCycle(path, at_next_pos, flow);
591 return true;
594 return false;
598 * Eliminate all cycles in the graph. Check paths starting at each node for
599 * potential cycles.
600 * @return If any cycles have been found and eliminated.
602 bool MCF1stPass::EliminateCycles()
604 bool cycles_found = false;
605 uint size = this->job.Size();
606 PathVector path(size, NULL);
607 for (NodeID node = 0; node < size; ++node) {
608 /* Starting at each node in the graph find all cycles involving this
609 * node. */
610 std::fill(path.begin(), path.end(), (Path *)NULL);
611 cycles_found |= this->EliminateCycles(path, node, node);
613 return cycles_found;
617 * Run the first pass of the MCF calculation.
618 * @param job Link graph job to calculate.
620 MCF1stPass::MCF1stPass(LinkGraphJob &job) : MultiCommodityFlow(job)
622 PathVector paths;
623 uint size = job.Size();
624 uint accuracy = job.Settings().accuracy;
625 bool more_loops;
627 do {
628 more_loops = false;
629 for (NodeID source = 0; source < size; ++source) {
630 /* First saturate the shortest paths. */
631 this->Dijkstra<DistanceAnnotation, GraphEdgeIterator>(source, paths);
633 for (NodeID dest = 0; dest < size; ++dest) {
634 Edge edge = job[source][dest];
635 if (edge.UnsatisfiedDemand() > 0) {
636 Path *path = paths[dest];
637 assert(path != NULL);
638 /* Generally only allow paths that don't exceed the
639 * available capacity. But if no demand has been assigned
640 * yet, make an exception and allow any valid path *once*. */
641 if (path->GetFreeCapacity() > 0 && this->PushFlow(edge, path,
642 accuracy, this->max_saturation) > 0) {
643 /* If a path has been found there is a chance we can
644 * find more. */
645 more_loops = more_loops || (edge.UnsatisfiedDemand() > 0);
647 else if (edge.UnsatisfiedDemand() == edge.Demand() &&
648 path->GetFreeCapacity() > INT_MIN) {
649 this->PushFlow(edge, path, accuracy, UINT_MAX);
653 this->CleanupPaths(source, paths);
655 } while ((more_loops || this->EliminateCycles()) && !job.IsJobAborted());
659 * Run the second pass of the MCF calculation which assigns all remaining
660 * demands to existing paths.
661 * @param job Link graph job to calculate.
663 MCF2ndPass::MCF2ndPass(LinkGraphJob &job) : MultiCommodityFlow(job)
665 this->max_saturation = UINT_MAX; // disable artificial cap on saturation
666 PathVector paths;
667 uint size = job.Size();
668 uint accuracy = job.Settings().accuracy;
669 bool demand_left = true;
670 while (demand_left && !job.IsJobAborted()) {
671 demand_left = false;
672 for (NodeID source = 0; source < size; ++source) {
673 this->Dijkstra<CapacityAnnotation, FlowEdgeIterator>(source, paths);
674 for (NodeID dest = 0; dest < size; ++dest) {
675 Edge edge = this->job[source][dest];
676 Path *path = paths[dest];
677 if (edge.UnsatisfiedDemand() > 0 && path->GetFreeCapacity() > INT_MIN) {
678 this->PushFlow(edge, path, accuracy, UINT_MAX);
679 if (edge.UnsatisfiedDemand() > 0) demand_left = true;
682 this->CleanupPaths(source, paths);
688 * Relation that creates a weak order without duplicates.
689 * Avoid accidentally deleting different paths of the same capacity/distance in
690 * a set. When the annotation is the same node IDs are compared, so there are
691 * no equal ranges.
692 * @tparam T Type to be compared on.
693 * @param x_anno First value.
694 * @param y_anno Second value.
695 * @param x Node id associated with the first value.
696 * @param y Node id associated with the second value.
698 template <typename T>
699 bool Greater(T x_anno, T y_anno, NodeID x, NodeID y)
701 if (x_anno > y_anno) return true;
702 if (x_anno < y_anno) return false;
703 return x > y;
707 * Compare two capacity annotations.
708 * @param x First capacity annotation.
709 * @param y Second capacity annotation.
710 * @return If x is better than y.
712 bool CapacityAnnotation::Comparator::operator()(const AnnoSetItem<CapacityAnnotation> &x,
713 const AnnoSetItem<CapacityAnnotation> &y) const
715 return x.anno_ptr != y.anno_ptr && Greater<int>(x.cached_annotation, y.cached_annotation,
716 x.node_id, y.node_id);
720 * Compare two distance annotations.
721 * @param x First distance annotation.
722 * @param y Second distance annotation.
723 * @return If x is better than y.
725 bool DistanceAnnotation::Comparator::operator()(const AnnoSetItem<DistanceAnnotation> &x,
726 const AnnoSetItem<DistanceAnnotation> &y) const
728 return x.anno_ptr != y.anno_ptr && !Greater<uint>(x.cached_annotation, y.cached_annotation,
729 x.node_id, y.node_id);