Linkgraph: Replace RB-tree with B-tree in MCF Dijkstra
This commit is contained in:
@@ -277,10 +277,10 @@ void Path::Fork(Path *base, uint cap, int free_cap, uint dist)
|
|||||||
this->free_capacity = min(base->free_capacity, free_cap);
|
this->free_capacity = min(base->free_capacity, free_cap);
|
||||||
this->distance = base->distance + dist;
|
this->distance = base->distance + dist;
|
||||||
assert(this->distance > 0);
|
assert(this->distance > 0);
|
||||||
if (this->parent != base) {
|
if (this->GetParent() != base) {
|
||||||
this->Detach();
|
this->Detach();
|
||||||
this->parent = base;
|
this->SetParent(base);
|
||||||
this->parent->num_children++;
|
base->num_children++;
|
||||||
}
|
}
|
||||||
this->origin = base->origin;
|
this->origin = base->origin;
|
||||||
}
|
}
|
||||||
@@ -295,8 +295,8 @@ void Path::Fork(Path *base, uint cap, int free_cap, uint dist)
|
|||||||
*/
|
*/
|
||||||
uint Path::AddFlow(uint new_flow, LinkGraphJob &job, uint max_saturation)
|
uint Path::AddFlow(uint new_flow, LinkGraphJob &job, uint max_saturation)
|
||||||
{
|
{
|
||||||
if (this->parent != nullptr) {
|
if (this->GetParent() != nullptr) {
|
||||||
LinkGraphJob::Edge edge = job[this->parent->node][this->node];
|
LinkGraphJob::Edge edge = job[this->GetParent()->node][this->node];
|
||||||
if (max_saturation != UINT_MAX) {
|
if (max_saturation != UINT_MAX) {
|
||||||
uint usable_cap = edge.Capacity() * max_saturation / 100;
|
uint usable_cap = edge.Capacity() * max_saturation / 100;
|
||||||
if (usable_cap > edge.Flow()) {
|
if (usable_cap > edge.Flow()) {
|
||||||
@@ -305,9 +305,9 @@ uint Path::AddFlow(uint new_flow, LinkGraphJob &job, uint max_saturation)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
new_flow = this->parent->AddFlow(new_flow, job, max_saturation);
|
new_flow = this->GetParent()->AddFlow(new_flow, job, max_saturation);
|
||||||
if (this->flow == 0 && new_flow > 0) {
|
if (this->flow == 0 && new_flow > 0) {
|
||||||
job[this->parent->node].Paths().push_back(this);
|
job[this->GetParent()->node].Paths().push_back(this);
|
||||||
}
|
}
|
||||||
edge.AddFlow(new_flow);
|
edge.AddFlow(new_flow);
|
||||||
}
|
}
|
||||||
@@ -325,6 +325,6 @@ Path::Path(NodeID n, bool source) :
|
|||||||
capacity(source ? UINT_MAX : 0),
|
capacity(source ? UINT_MAX : 0),
|
||||||
free_capacity(source ? INT_MAX : INT_MIN),
|
free_capacity(source ? INT_MAX : INT_MIN),
|
||||||
flow(0), node(n), origin(source ? n : INVALID_NODE),
|
flow(0), node(n), origin(source ? n : INVALID_NODE),
|
||||||
num_children(0), parent(nullptr)
|
num_children(0), parent_storage(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@@ -393,7 +393,7 @@ public:
|
|||||||
inline NodeID GetOrigin() const { return this->origin; }
|
inline NodeID GetOrigin() const { return this->origin; }
|
||||||
|
|
||||||
/** Get the parent leg of this one. */
|
/** Get the parent leg of this one. */
|
||||||
inline Path *GetParent() { return this->parent; }
|
inline Path *GetParent() { return reinterpret_cast<Path *>(this->parent_storage & ~1); }
|
||||||
|
|
||||||
/** Get the overall capacity of the path. */
|
/** Get the overall capacity of the path. */
|
||||||
inline uint GetCapacity() const { return this->capacity; }
|
inline uint GetCapacity() const { return this->capacity; }
|
||||||
@@ -442,15 +442,18 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline void Detach()
|
inline void Detach()
|
||||||
{
|
{
|
||||||
if (this->parent != nullptr) {
|
if (this->GetParent() != nullptr) {
|
||||||
this->parent->num_children--;
|
this->GetParent()->num_children--;
|
||||||
this->parent = nullptr;
|
this->SetParent(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint AddFlow(uint f, LinkGraphJob &job, uint max_saturation);
|
uint AddFlow(uint f, LinkGraphJob &job, uint max_saturation);
|
||||||
void Fork(Path *base, uint cap, int free_cap, uint dist);
|
void Fork(Path *base, uint cap, int free_cap, uint dist);
|
||||||
|
|
||||||
|
inline bool GetAnnosSetFlag() const { return HasBit(this->parent_storage, 0); }
|
||||||
|
inline void SetAnnosSetFlag(bool flag) { SB(this->parent_storage, 0, 1, flag ? 1 : 0); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -469,7 +472,11 @@ protected:
|
|||||||
NodeID node; ///< Link graph node this leg passes.
|
NodeID node; ///< Link graph node this leg passes.
|
||||||
NodeID origin; ///< Link graph node this path originates from.
|
NodeID origin; ///< Link graph node this path originates from.
|
||||||
uint num_children; ///< Number of child legs that have been forked from this path.
|
uint num_children; ///< Number of child legs that have been forked from this path.
|
||||||
Path *parent; ///< Parent leg of this one.
|
|
||||||
|
uintptr_t parent_storage; ///< Parent leg of this one, flag in LSB of pointer
|
||||||
|
|
||||||
|
/** Get the parent leg of this one. */
|
||||||
|
inline void SetParent(Path *parent) { this->parent_storage = reinterpret_cast<uintptr_t>(parent) | (this->parent_storage & 1); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* LINKGRAPHJOB_H */
|
#endif /* LINKGRAPHJOB_H */
|
||||||
|
@@ -22,6 +22,7 @@ public:
|
|||||||
NodeID node_id;
|
NodeID node_id;
|
||||||
|
|
||||||
AnnoSetItem(Tannotation *anno) : anno_ptr(anno), cached_annotation(anno->GetAnnotation()), node_id(anno->GetNode()) {}
|
AnnoSetItem(Tannotation *anno) : anno_ptr(anno), cached_annotation(anno->GetAnnotation()), node_id(anno->GetNode()) {}
|
||||||
|
AnnoSetItem() : anno_ptr(nullptr), cached_annotation(0), node_id(INVALID_NODE) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -263,98 +264,6 @@ bool CapacityAnnotation::IsBetter(const CapacityAnnotation *base, uint cap,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CUSTOM_ALLOCATOR
|
|
||||||
/**
|
|
||||||
* Storage for AnnoSetAllocator instances
|
|
||||||
*/
|
|
||||||
struct AnnoSetAllocatorStore {
|
|
||||||
std::vector<void *> used_blocks;
|
|
||||||
void *current_block;
|
|
||||||
size_t next_position;
|
|
||||||
void *last_freed;
|
|
||||||
|
|
||||||
AnnoSetAllocatorStore() : current_block(nullptr), next_position(0), last_freed(nullptr) {}
|
|
||||||
|
|
||||||
~AnnoSetAllocatorStore()
|
|
||||||
{
|
|
||||||
for (std::vector<void *>::iterator i = used_blocks.begin(); i != used_blocks.end(); ++i) {
|
|
||||||
free(*i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom allocator specifically for use with MultiCommodityFlow::Dijkstra::AnnoSet
|
|
||||||
* This allocates RB-set nodes in contiguous blocks, and frees all allocated nodes when destructed
|
|
||||||
* If a node is deallocated, it is returned in the next allocation, this is so that the same node
|
|
||||||
* can be re-used across a call to Path::Fork
|
|
||||||
*/
|
|
||||||
template<class Ttype>
|
|
||||||
struct AnnoSetAllocator {
|
|
||||||
static const size_t block_size = 1024;
|
|
||||||
|
|
||||||
AnnoSetAllocatorStore &store;
|
|
||||||
|
|
||||||
void NewBlock()
|
|
||||||
{
|
|
||||||
store.current_block = MallocT<Ttype>(block_size);
|
|
||||||
store.next_position = 0;
|
|
||||||
store.used_blocks.push_back(store.current_block);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef Ttype value_type;
|
|
||||||
|
|
||||||
template<typename Tother>
|
|
||||||
struct rebind {
|
|
||||||
typedef AnnoSetAllocator<Tother> other;
|
|
||||||
};
|
|
||||||
|
|
||||||
AnnoSetAllocator(AnnoSetAllocatorStore &store) : store(store) {}
|
|
||||||
|
|
||||||
template<typename Tother>
|
|
||||||
AnnoSetAllocator(const AnnoSetAllocator<Tother> &other) : store(other.store) {}
|
|
||||||
|
|
||||||
Ttype* allocate(size_t n)
|
|
||||||
{
|
|
||||||
if (store.current_block == nullptr) NewBlock();
|
|
||||||
|
|
||||||
assert(n == 1);
|
|
||||||
if (store.last_freed != nullptr) {
|
|
||||||
Ttype* out = static_cast<Ttype*>(store.last_freed);
|
|
||||||
store.last_freed = nullptr;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (store.next_position == block_size) NewBlock();
|
|
||||||
|
|
||||||
Ttype* next = static_cast<Ttype*>(store.current_block) + store.next_position;
|
|
||||||
store.next_position++;
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate(Ttype* p, size_t n)
|
|
||||||
{
|
|
||||||
store.last_freed = p;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation wrapper class which also stores an iterator to the AnnoSet node which points to this annotation
|
|
||||||
* This is to enable erasing the AnnoSet node when calling Path::Fork without having to search the set
|
|
||||||
*/
|
|
||||||
template<class Tannotation>
|
|
||||||
struct AnnosWrapper : public Tannotation {
|
|
||||||
#ifdef CUSTOM_ALLOCATOR
|
|
||||||
typename std::set<AnnoSetItem<Tannotation>, typename Tannotation::Comparator, AnnoSetAllocator<AnnoSetItem<Tannotation> > >::iterator self_iter;
|
|
||||||
#else
|
|
||||||
typename std::set<AnnoSetItem<Tannotation>, typename Tannotation::Comparator>::iterator self_iter;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
AnnosWrapper(NodeID n, bool source = false) : Tannotation(n, source) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A slightly modified Dijkstra algorithm. Grades the paths not necessarily by
|
* A slightly modified Dijkstra algorithm. Grades the paths not necessarily by
|
||||||
* distance, but by the value Tannotation computes. It uses the max_saturation
|
* distance, but by the value Tannotation computes. It uses the max_saturation
|
||||||
@@ -367,31 +276,27 @@ struct AnnosWrapper : public Tannotation {
|
|||||||
template<class Tannotation, class Tedge_iterator>
|
template<class Tannotation, class Tedge_iterator>
|
||||||
void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths)
|
void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths)
|
||||||
{
|
{
|
||||||
#ifdef CUSTOM_ALLOCATOR
|
typedef btree::btree_set<AnnoSetItem<Tannotation>, typename Tannotation::Comparator> AnnoSet;
|
||||||
typedef std::set<AnnoSetItem<Tannotation>, typename Tannotation::Comparator, AnnoSetAllocator<AnnoSetItem<Tannotation> > > AnnoSet;
|
|
||||||
AnnoSetAllocatorStore annos_store;
|
|
||||||
AnnoSet annos = AnnoSet(typename Tannotation::Comparator(), AnnoSetAllocator<Tannotation *>(annos_store));
|
|
||||||
#else
|
|
||||||
typedef std::set<AnnoSetItem<Tannotation>, typename Tannotation::Comparator> AnnoSet;
|
|
||||||
AnnoSet annos = AnnoSet(typename Tannotation::Comparator());
|
AnnoSet annos = AnnoSet(typename Tannotation::Comparator());
|
||||||
#endif
|
|
||||||
Tedge_iterator iter(this->job);
|
Tedge_iterator iter(this->job);
|
||||||
uint size = this->job.Size();
|
uint size = this->job.Size();
|
||||||
paths.resize(size, nullptr);
|
paths.resize(size, nullptr);
|
||||||
|
|
||||||
this->job.path_allocator.SetParameters(sizeof(AnnosWrapper<Tannotation>), (8192 - 32) / sizeof(AnnosWrapper<Tannotation>));
|
this->job.path_allocator.SetParameters(sizeof(Tannotation), (8192 - 32) / sizeof(Tannotation));
|
||||||
|
|
||||||
for (NodeID node = 0; node < size; ++node) {
|
for (NodeID node = 0; node < size; ++node) {
|
||||||
AnnosWrapper<Tannotation> *anno = new (this->job.path_allocator.Allocate()) AnnosWrapper<Tannotation>(node, node == source_node);
|
Tannotation *anno = new (this->job.path_allocator.Allocate()) Tannotation(node, node == source_node);
|
||||||
anno->UpdateAnnotation();
|
anno->UpdateAnnotation();
|
||||||
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
|
if (node == source_node) {
|
||||||
|
annos.insert(AnnoSetItem<Tannotation>(anno)).first;
|
||||||
|
anno->SetAnnosSetFlag(true);
|
||||||
|
}
|
||||||
paths[node] = anno;
|
paths[node] = anno;
|
||||||
}
|
}
|
||||||
while (!annos.empty()) {
|
while (!annos.empty()) {
|
||||||
typename AnnoSet::iterator i = annos.begin();
|
typename AnnoSet::iterator i = annos.begin();
|
||||||
AnnosWrapper<Tannotation> *source = static_cast<AnnosWrapper<Tannotation> *>(i->anno_ptr);
|
Tannotation *source = i->anno_ptr;
|
||||||
annos.erase(i);
|
annos.erase(i);
|
||||||
source->self_iter = annos.end();
|
|
||||||
NodeID from = source->GetNode();
|
NodeID from = source->GetNode();
|
||||||
iter.SetNode(source_node, from);
|
iter.SetNode(source_node, from);
|
||||||
for (NodeID to = iter.Next(); to != INVALID_NODE; to = iter.Next()) {
|
for (NodeID to = iter.Next(); to != INVALID_NODE; to = iter.Next()) {
|
||||||
@@ -405,12 +310,13 @@ void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths)
|
|||||||
}
|
}
|
||||||
/* punish in-between stops a little */
|
/* punish in-between stops a little */
|
||||||
uint distance = DistanceMaxPlusManhattan(this->job[from].XY(), this->job[to].XY()) + 1;
|
uint distance = DistanceMaxPlusManhattan(this->job[from].XY(), this->job[to].XY()) + 1;
|
||||||
AnnosWrapper<Tannotation> *dest = static_cast<AnnosWrapper<Tannotation> *>(paths[to]);
|
Tannotation *dest = static_cast<Tannotation *>(paths[to]);
|
||||||
if (dest->IsBetter(source, capacity, capacity - edge.Flow(), distance)) {
|
if (dest->IsBetter(source, capacity, capacity - edge.Flow(), distance)) {
|
||||||
if (dest->self_iter != annos.end()) annos.erase(dest->self_iter);
|
if (dest->GetAnnosSetFlag()) annos.erase(AnnoSetItem<Tannotation>(dest));
|
||||||
dest->Fork(source, capacity, capacity - edge.Flow(), distance);
|
dest->Fork(source, capacity, capacity - edge.Flow(), distance);
|
||||||
dest->UpdateAnnotation();
|
dest->UpdateAnnotation();
|
||||||
dest->self_iter = annos.insert(AnnoSetItem<Tannotation>(dest)).first;
|
annos.insert(AnnoSetItem<Tannotation>(dest));
|
||||||
|
dest->SetAnnosSetFlag(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user