diff --git a/src/linkgraph/demands.cpp b/src/linkgraph/demands.cpp index b7afe51737..71af952f09 100644 --- a/src/linkgraph/demands.cpp +++ b/src/linkgraph/demands.cpp @@ -244,7 +244,8 @@ void AsymmetricScalerEq::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to */ inline void Scaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id, uint demand_forw) { - job[from_id].DeliverSupply(to_id, demand_forw); + job[from_id].DeliverSupply(demand_forw); + job.demand_map[std::make_pair(from_id, to_id)] += demand_forw; } /** @@ -476,4 +477,25 @@ DemandCalculator::DemandCalculator(LinkGraphJob &job) : first_unseen++; } } while (first_unseen < size); + + if (job.demand_map.size() > 0) { + job.demand_annotation_store.resize(job.demand_map.size()); + size_t start_idx = 0; + size_t idx = 0; + NodeID last_from = job.demand_map.begin()->first.first; + auto flush = [&]() { + job[last_from].SetDemandAnnotations({ job.demand_annotation_store.data() + start_idx, idx - start_idx }); + }; + for (auto &iter : job.demand_map) { + if (iter.first.first != last_from) { + flush(); + last_from = iter.first.first; + start_idx = idx; + } + job.demand_annotation_store[idx] = { iter.first.second, iter.second, iter.second }; + idx++; + } + flush(); + job.demand_map.clear(); + } } diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index ad3e55cb8f..8c13b31748 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -212,9 +212,7 @@ void LinkGraphJob::Init() */ void LinkGraphJob::EdgeAnnotation::Init() { - this->demand = 0; this->flow = 0; - this->unsatisfied_demand = 0; } /** diff --git a/src/linkgraph/linkgraphjob.h b/src/linkgraph/linkgraphjob.h index 1b0341021b..1102c0e515 100644 --- a/src/linkgraph/linkgraphjob.h +++ b/src/linkgraph/linkgraphjob.h @@ -31,13 +31,21 @@ extern LinkGraphJobPool _link_graph_job_pool; * Class for calculation jobs to be run on link graphs. */ class LinkGraphJob : public LinkGraphJobPool::PoolItem<&_link_graph_job_pool>{ +public: + /** + * Annotation for a link graph demand edge. + */ + struct DemandAnnotation { + NodeID dest; ///< Target node + uint demand = 0; ///< Transport demand between the nodes. + uint unsatisfied_demand = 0; ///< Demand over this edge that hasn't been satisfied yet. + }; + private: /** - * Annotation for a link graph edge. + * Annotation for a link graph flow edge. */ struct EdgeAnnotation { - uint demand; ///< Transport demand between the nodes. - uint unsatisfied_demand; ///< Demand over this edge that hasn't been satisfied yet. uint flow; ///< Planned flow over this edge. void Init(); }; @@ -50,6 +58,7 @@ private: uint received_demand; ///< Received demand towards this node. PathList paths; ///< Paths through this node, sorted so that those with flow == 0 are in the back. FlowStatMap flows; ///< Planned flows to other nodes. + span demands; ///< Demand annotations belonging to this node. void Init(uint supply); }; @@ -79,6 +88,9 @@ protected: public: + btree::btree_map, uint> demand_map; ///< Demand map. + std::vector demand_annotation_store; ///< Demand annotation store. + DynUniformArenaAllocator path_allocator; ///< Arena allocator used for paths /** @@ -96,18 +108,6 @@ public: AnnoEdge(EdgeAnnotation &anno) : anno(anno) {} - /** - * Get the transport demand between end the points of the edge. - * @return Demand. - */ - uint Demand() const { return this->anno.demand; } - - /** - * Get the transport demand that hasn't been satisfied by flows, yet. - * @return Unsatisfied demand. - */ - uint UnsatisfiedDemand() const { return this->anno.unsatisfied_demand; } - /** * Get the total flow on the edge. * @return Flow. @@ -129,26 +129,6 @@ public: assert(flow <= this->anno.flow); this->anno.flow -= flow; } - - /** - * Add some (not yet satisfied) demand. - * @param demand Demand to be added. - */ - void AddDemand(uint demand) - { - this->anno.demand += demand; - this->anno.unsatisfied_demand += demand; - } - - /** - * Satisfy some demand. - * @param demand Demand to be satisfied. - */ - void SatisfyDemand(uint demand) - { - assert(demand <= this->anno.unsatisfied_demand); - this->anno.unsatisfied_demand -= demand; - } }; /** @@ -237,13 +217,11 @@ public: /** * Deliver some supply, adding demand to the respective edge. - * @param to Destination for supply. * @param amount Amount of supply to be delivered. */ - void DeliverSupply(NodeID to, uint amount) + void DeliverSupply(uint amount) { this->node_anno.undelivered_supply -= amount; - (*this)[to].AddDemand(amount); } /** @@ -254,6 +232,16 @@ public: { this->node_anno.received_demand += amount; } + + span GetDemandAnnotations() const + { + return this->node_anno.demands; + } + + void SetDemandAnnotations(span demands) + { + this->node_anno.demands = demands; + } }; /** diff --git a/src/linkgraph/linkgraphjob_base.h b/src/linkgraph/linkgraphjob_base.h index a28c3bbcfc..b864589d6b 100644 --- a/src/linkgraph/linkgraphjob_base.h +++ b/src/linkgraph/linkgraphjob_base.h @@ -17,5 +17,6 @@ typedef LinkGraphJob::Node Node; typedef LinkGraphJob::Edge Edge; typedef LinkGraphJob::AnnoEdge AnnoEdge; +typedef LinkGraphJob::DemandAnnotation DemandAnnotation; #endif /* LINKGRAPHJOB_BASE_H */ diff --git a/src/linkgraph/mcf.cpp b/src/linkgraph/mcf.cpp index 938f6b2c9e..21219fac85 100644 --- a/src/linkgraph/mcf.cpp +++ b/src/linkgraph/mcf.cpp @@ -391,20 +391,20 @@ void MultiCommodityFlow::CleanupPaths(NodeID source_id, PathVector &paths) /** * Push flow along a path and update the unsatisfied_demand of the associated * edge. - * @param edge Edge whose ends the path connects. + * @param anno Distance annotation whose ends the path connects. * @param path End of the path the flow should be pushed on. * @param min_step_size Minimum flow size. * @param accuracy Accuracy of the calculation. * @param max_saturation If < UINT_MAX only push flow up to the given * saturation, otherwise the path can be "overloaded". */ -uint MultiCommodityFlow::PushFlow(AnnoEdge &edge, Path *path, uint min_step_size, uint accuracy, +uint MultiCommodityFlow::PushFlow(DemandAnnotation &anno, Path *path, uint min_step_size, uint accuracy, uint max_saturation) { - assert(edge.UnsatisfiedDemand() > 0); - uint flow = std::min(std::max(edge.Demand() / accuracy, min_step_size), edge.UnsatisfiedDemand()); + dbg_assert(anno.unsatisfied_demand > 0); + uint flow = std::min(std::max(anno.demand / accuracy, min_step_size), anno.unsatisfied_demand); flow = path->AddFlow(flow, this->job, max_saturation); - edge.SatisfyDemand(flow); + anno.unsatisfied_demand -= flow; return flow; } @@ -571,10 +571,10 @@ MCF1stPass::MCF1stPass(LinkGraphJob &job) : MultiCommodityFlow(job) uint64 total_demand = 0; uint demand_count = 0; for (NodeID source = 0; source < size; ++source) { - for (NodeID dest = 0; dest < size; ++dest) { - AnnoEdge edge = job[source][dest]; - if (edge.UnsatisfiedDemand() > 0) { - total_demand += edge.UnsatisfiedDemand(); + const Node &node = job[source]; + for (const DemandAnnotation &anno : node.GetDemandAnnotations()) { + if (anno.unsatisfied_demand > 0) { + total_demand += anno.unsatisfied_demand; demand_count++; } } @@ -593,24 +593,24 @@ MCF1stPass::MCF1stPass(LinkGraphJob &job) : MultiCommodityFlow(job) this->Dijkstra(source, paths); bool source_demand_left = false; - for (NodeID dest = 0; dest < size; ++dest) { - AnnoEdge edge = job[source][dest]; - if (edge.UnsatisfiedDemand() > 0) { + for (DemandAnnotation &anno : job[source].GetDemandAnnotations()) { + NodeID dest = anno.dest; + if (anno.unsatisfied_demand > 0) { Path *path = paths[dest]; assert(path != nullptr); /* Generally only allow paths that don't exceed the * available capacity. But if no demand has been assigned * yet, make an exception and allow any valid path *once*. */ - if (path->GetFreeCapacity() > 0 && this->PushFlow(edge, path, + if (path->GetFreeCapacity() > 0 && this->PushFlow(anno, path, min_step_size, accuracy, this->max_saturation) > 0) { /* If a path has been found there is a chance we can * find more. */ - more_loops = more_loops || (edge.UnsatisfiedDemand() > 0); - } else if (edge.UnsatisfiedDemand() == edge.Demand() && + more_loops = more_loops || (anno.unsatisfied_demand > 0); + } else if (anno.unsatisfied_demand == anno.demand && path->GetFreeCapacity() > INT_MIN) { - this->PushFlow(edge, path, min_step_size, accuracy, UINT_MAX); + this->PushFlow(anno, path, min_step_size, accuracy, UINT_MAX); } - if (edge.UnsatisfiedDemand() > 0) source_demand_left = true; + if (anno.unsatisfied_demand > 0) source_demand_left = true; } } if (!source_demand_left) finished_sources[source] = true; @@ -640,12 +640,12 @@ MCF2ndPass::MCF2ndPass(LinkGraphJob &job) : MultiCommodityFlow(job) this->Dijkstra(source, paths); bool source_demand_left = false; - for (NodeID dest = 0; dest < size; ++dest) { - AnnoEdge edge = this->job[source][dest]; - Path *path = paths[dest]; - if (edge.UnsatisfiedDemand() > 0 && path->GetFreeCapacity() > INT_MIN) { - this->PushFlow(edge, path, 1, accuracy, UINT_MAX); - if (edge.UnsatisfiedDemand() > 0) { + for (DemandAnnotation &anno : this->job[source].GetDemandAnnotations()) { + if (anno.unsatisfied_demand == 0) continue; + Path *path = paths[anno.dest]; + if (path->GetFreeCapacity() > INT_MIN) { + this->PushFlow(anno, path, 1, accuracy, UINT_MAX); + if (anno.unsatisfied_demand > 0) { demand_left = true; source_demand_left = true; } diff --git a/src/linkgraph/mcf.h b/src/linkgraph/mcf.h index 8714ce1474..06f344e375 100644 --- a/src/linkgraph/mcf.h +++ b/src/linkgraph/mcf.h @@ -24,7 +24,7 @@ protected: template void Dijkstra(NodeID from, PathVector &paths); - uint PushFlow(AnnoEdge &edge, Path *path, uint min_step_size, uint accuracy, uint max_saturation); + uint PushFlow(DemandAnnotation &anno, Path *path, uint min_step_size, uint accuracy, uint max_saturation); void CleanupPaths(NodeID source, PathVector &paths);