Linkgraph: Split demand annotations from edge flow annotations
Use sparse storage format for demand annotations
This commit is contained in:
@@ -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)
|
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++;
|
first_unseen++;
|
||||||
}
|
}
|
||||||
} while (first_unseen < size);
|
} 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -212,9 +212,7 @@ void LinkGraphJob::Init()
|
|||||||
*/
|
*/
|
||||||
void LinkGraphJob::EdgeAnnotation::Init()
|
void LinkGraphJob::EdgeAnnotation::Init()
|
||||||
{
|
{
|
||||||
this->demand = 0;
|
|
||||||
this->flow = 0;
|
this->flow = 0;
|
||||||
this->unsatisfied_demand = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -31,13 +31,21 @@ extern LinkGraphJobPool _link_graph_job_pool;
|
|||||||
* Class for calculation jobs to be run on link graphs.
|
* Class for calculation jobs to be run on link graphs.
|
||||||
*/
|
*/
|
||||||
class LinkGraphJob : public LinkGraphJobPool::PoolItem<&_link_graph_job_pool>{
|
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:
|
private:
|
||||||
/**
|
/**
|
||||||
* Annotation for a link graph edge.
|
* Annotation for a link graph flow edge.
|
||||||
*/
|
*/
|
||||||
struct EdgeAnnotation {
|
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.
|
uint flow; ///< Planned flow over this edge.
|
||||||
void Init();
|
void Init();
|
||||||
};
|
};
|
||||||
@@ -50,6 +58,7 @@ private:
|
|||||||
uint received_demand; ///< Received demand towards this node.
|
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.
|
PathList paths; ///< Paths through this node, sorted so that those with flow == 0 are in the back.
|
||||||
FlowStatMap flows; ///< Planned flows to other nodes.
|
FlowStatMap flows; ///< Planned flows to other nodes.
|
||||||
|
span<DemandAnnotation> demands; ///< Demand annotations belonging to this node.
|
||||||
void Init(uint supply);
|
void Init(uint supply);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,6 +88,9 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
btree::btree_map<std::pair<NodeID, NodeID>, uint> demand_map; ///< Demand map.
|
||||||
|
std::vector<DemandAnnotation> demand_annotation_store; ///< Demand annotation store.
|
||||||
|
|
||||||
DynUniformArenaAllocator path_allocator; ///< Arena allocator used for paths
|
DynUniformArenaAllocator path_allocator; ///< Arena allocator used for paths
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,18 +108,6 @@ public:
|
|||||||
AnnoEdge(EdgeAnnotation &anno) :
|
AnnoEdge(EdgeAnnotation &anno) :
|
||||||
anno(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.
|
* Get the total flow on the edge.
|
||||||
* @return Flow.
|
* @return Flow.
|
||||||
@@ -129,26 +129,6 @@ public:
|
|||||||
assert(flow <= this->anno.flow);
|
assert(flow <= this->anno.flow);
|
||||||
this->anno.flow -= 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.
|
* Deliver some supply, adding demand to the respective edge.
|
||||||
* @param to Destination for supply.
|
|
||||||
* @param amount Amount of supply to be delivered.
|
* @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->node_anno.undelivered_supply -= amount;
|
||||||
(*this)[to].AddDemand(amount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -254,6 +232,16 @@ public:
|
|||||||
{
|
{
|
||||||
this->node_anno.received_demand += amount;
|
this->node_anno.received_demand += amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span<DemandAnnotation> GetDemandAnnotations() const
|
||||||
|
{
|
||||||
|
return this->node_anno.demands;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetDemandAnnotations(span<DemandAnnotation> demands)
|
||||||
|
{
|
||||||
|
this->node_anno.demands = demands;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -17,5 +17,6 @@
|
|||||||
typedef LinkGraphJob::Node Node;
|
typedef LinkGraphJob::Node Node;
|
||||||
typedef LinkGraphJob::Edge Edge;
|
typedef LinkGraphJob::Edge Edge;
|
||||||
typedef LinkGraphJob::AnnoEdge AnnoEdge;
|
typedef LinkGraphJob::AnnoEdge AnnoEdge;
|
||||||
|
typedef LinkGraphJob::DemandAnnotation DemandAnnotation;
|
||||||
|
|
||||||
#endif /* LINKGRAPHJOB_BASE_H */
|
#endif /* LINKGRAPHJOB_BASE_H */
|
||||||
|
@@ -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
|
* Push flow along a path and update the unsatisfied_demand of the associated
|
||||||
* edge.
|
* 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 path End of the path the flow should be pushed on.
|
||||||
* @param min_step_size Minimum flow size.
|
* @param min_step_size Minimum flow size.
|
||||||
* @param accuracy Accuracy of the calculation.
|
* @param accuracy Accuracy of the calculation.
|
||||||
* @param max_saturation If < UINT_MAX only push flow up to the given
|
* @param max_saturation If < UINT_MAX only push flow up to the given
|
||||||
* saturation, otherwise the path can be "overloaded".
|
* 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)
|
uint max_saturation)
|
||||||
{
|
{
|
||||||
assert(edge.UnsatisfiedDemand() > 0);
|
dbg_assert(anno.unsatisfied_demand > 0);
|
||||||
uint flow = std::min(std::max(edge.Demand() / accuracy, min_step_size), edge.UnsatisfiedDemand());
|
uint flow = std::min(std::max(anno.demand / accuracy, min_step_size), anno.unsatisfied_demand);
|
||||||
flow = path->AddFlow(flow, this->job, max_saturation);
|
flow = path->AddFlow(flow, this->job, max_saturation);
|
||||||
edge.SatisfyDemand(flow);
|
anno.unsatisfied_demand -= flow;
|
||||||
return flow;
|
return flow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,10 +571,10 @@ MCF1stPass::MCF1stPass(LinkGraphJob &job) : MultiCommodityFlow(job)
|
|||||||
uint64 total_demand = 0;
|
uint64 total_demand = 0;
|
||||||
uint demand_count = 0;
|
uint demand_count = 0;
|
||||||
for (NodeID source = 0; source < size; ++source) {
|
for (NodeID source = 0; source < size; ++source) {
|
||||||
for (NodeID dest = 0; dest < size; ++dest) {
|
const Node &node = job[source];
|
||||||
AnnoEdge edge = job[source][dest];
|
for (const DemandAnnotation &anno : node.GetDemandAnnotations()) {
|
||||||
if (edge.UnsatisfiedDemand() > 0) {
|
if (anno.unsatisfied_demand > 0) {
|
||||||
total_demand += edge.UnsatisfiedDemand();
|
total_demand += anno.unsatisfied_demand;
|
||||||
demand_count++;
|
demand_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -593,24 +593,24 @@ MCF1stPass::MCF1stPass(LinkGraphJob &job) : MultiCommodityFlow(job)
|
|||||||
this->Dijkstra<DistanceAnnotation, GraphEdgeIterator>(source, paths);
|
this->Dijkstra<DistanceAnnotation, GraphEdgeIterator>(source, paths);
|
||||||
|
|
||||||
bool source_demand_left = false;
|
bool source_demand_left = false;
|
||||||
for (NodeID dest = 0; dest < size; ++dest) {
|
for (DemandAnnotation &anno : job[source].GetDemandAnnotations()) {
|
||||||
AnnoEdge edge = job[source][dest];
|
NodeID dest = anno.dest;
|
||||||
if (edge.UnsatisfiedDemand() > 0) {
|
if (anno.unsatisfied_demand > 0) {
|
||||||
Path *path = paths[dest];
|
Path *path = paths[dest];
|
||||||
assert(path != nullptr);
|
assert(path != nullptr);
|
||||||
/* Generally only allow paths that don't exceed the
|
/* Generally only allow paths that don't exceed the
|
||||||
* available capacity. But if no demand has been assigned
|
* available capacity. But if no demand has been assigned
|
||||||
* yet, make an exception and allow any valid path *once*. */
|
* 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) {
|
min_step_size, accuracy, this->max_saturation) > 0) {
|
||||||
/* If a path has been found there is a chance we can
|
/* If a path has been found there is a chance we can
|
||||||
* find more. */
|
* find more. */
|
||||||
more_loops = more_loops || (edge.UnsatisfiedDemand() > 0);
|
more_loops = more_loops || (anno.unsatisfied_demand > 0);
|
||||||
} else if (edge.UnsatisfiedDemand() == edge.Demand() &&
|
} else if (anno.unsatisfied_demand == anno.demand &&
|
||||||
path->GetFreeCapacity() > INT_MIN) {
|
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;
|
if (!source_demand_left) finished_sources[source] = true;
|
||||||
@@ -640,12 +640,12 @@ MCF2ndPass::MCF2ndPass(LinkGraphJob &job) : MultiCommodityFlow(job)
|
|||||||
this->Dijkstra<CapacityAnnotation, FlowEdgeIterator>(source, paths);
|
this->Dijkstra<CapacityAnnotation, FlowEdgeIterator>(source, paths);
|
||||||
|
|
||||||
bool source_demand_left = false;
|
bool source_demand_left = false;
|
||||||
for (NodeID dest = 0; dest < size; ++dest) {
|
for (DemandAnnotation &anno : this->job[source].GetDemandAnnotations()) {
|
||||||
AnnoEdge edge = this->job[source][dest];
|
if (anno.unsatisfied_demand == 0) continue;
|
||||||
Path *path = paths[dest];
|
Path *path = paths[anno.dest];
|
||||||
if (edge.UnsatisfiedDemand() > 0 && path->GetFreeCapacity() > INT_MIN) {
|
if (path->GetFreeCapacity() > INT_MIN) {
|
||||||
this->PushFlow(edge, path, 1, accuracy, UINT_MAX);
|
this->PushFlow(anno, path, 1, accuracy, UINT_MAX);
|
||||||
if (edge.UnsatisfiedDemand() > 0) {
|
if (anno.unsatisfied_demand > 0) {
|
||||||
demand_left = true;
|
demand_left = true;
|
||||||
source_demand_left = true;
|
source_demand_left = true;
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@ protected:
|
|||||||
template<class Tannotation, class Tedge_iterator>
|
template<class Tannotation, class Tedge_iterator>
|
||||||
void Dijkstra(NodeID from, PathVector &paths);
|
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);
|
void CleanupPaths(NodeID source, PathVector &paths);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user