Change LinkGraph::EdgeMatrix to a sparse storage format
Use a btree_map instead of a SmallMatrix. This avoids excessive storage requirements for large graphs, and overly expensive edge matrix resize operations. Remove next_edge iteration mechanism.
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
#include "../date_func.h"
|
||||
#include "../saveload/saveload_common.h"
|
||||
#include "linkgraph_type.h"
|
||||
#include "../3rdparty/cpp-btree/btree_map.h"
|
||||
#include <utility>
|
||||
|
||||
class LinkGraph;
|
||||
@@ -74,10 +75,23 @@ public:
|
||||
Date last_unrestricted_update; ///< When the unrestricted part of the link was last updated.
|
||||
Date last_restricted_update; ///< When the restricted part of the link was last updated.
|
||||
Date last_aircraft_update; ///< When aircraft capacity of the link was last updated.
|
||||
NodeID next_edge; ///< Destination of next valid edge starting at the same source node.
|
||||
void Init();
|
||||
|
||||
void Init()
|
||||
{
|
||||
this->capacity = 0;
|
||||
this->usage = 0;
|
||||
this->travel_time_sum = 0;
|
||||
this->last_unrestricted_update = INVALID_DATE;
|
||||
this->last_restricted_update = INVALID_DATE;
|
||||
this->last_aircraft_update = INVALID_DATE;
|
||||
}
|
||||
|
||||
BaseEdge() { this->Init(); }
|
||||
};
|
||||
|
||||
typedef std::vector<BaseNode> NodeVector;
|
||||
typedef btree::btree_map<std::pair<NodeID, NodeID>, BaseEdge> EdgeMatrix;
|
||||
|
||||
/**
|
||||
* Wrapper for an edge (const or not) allowing retrieval, but no modification.
|
||||
* @tparam Tedge Actual edge class, may be "const BaseEdge" or just "BaseEdge".
|
||||
@@ -85,7 +99,7 @@ public:
|
||||
template<typename Tedge>
|
||||
class EdgeWrapper {
|
||||
protected:
|
||||
Tedge &edge; ///< Actual edge to be used.
|
||||
Tedge *edge; ///< Actual edge to be used.
|
||||
|
||||
public:
|
||||
|
||||
@@ -93,73 +107,69 @@ public:
|
||||
* Wrap a an edge.
|
||||
* @param edge Edge to be wrapped.
|
||||
*/
|
||||
EdgeWrapper (Tedge &edge) : edge(edge) {}
|
||||
EdgeWrapper (Tedge &edge) : edge(&edge) {}
|
||||
|
||||
/**
|
||||
* Get edge's capacity.
|
||||
* @return Capacity.
|
||||
*/
|
||||
uint Capacity() const { return this->edge.capacity; }
|
||||
uint Capacity() const { return this->edge->capacity; }
|
||||
|
||||
/**
|
||||
* Get edge's usage.
|
||||
* @return Usage.
|
||||
*/
|
||||
uint Usage() const { return this->edge.usage; }
|
||||
uint Usage() const { return this->edge->usage; }
|
||||
|
||||
/**
|
||||
* Get edge's average travel time.
|
||||
* @return Travel time, in ticks.
|
||||
*/
|
||||
uint32 TravelTime() const { return this->edge.travel_time_sum / this->edge.capacity; }
|
||||
uint32 TravelTime() const { return this->edge->travel_time_sum / this->edge->capacity; }
|
||||
|
||||
/**
|
||||
* Get the date of the last update to the edge's unrestricted capacity.
|
||||
* @return Last update.
|
||||
*/
|
||||
Date LastUnrestrictedUpdate() const { return this->edge.last_unrestricted_update; }
|
||||
Date LastUnrestrictedUpdate() const { return this->edge->last_unrestricted_update; }
|
||||
|
||||
/**
|
||||
* Get the date of the last update to the edge's restricted capacity.
|
||||
* @return Last update.
|
||||
*/
|
||||
Date LastRestrictedUpdate() const { return this->edge.last_restricted_update; }
|
||||
Date LastRestrictedUpdate() const { return this->edge->last_restricted_update; }
|
||||
|
||||
/**
|
||||
* Get the date of the last update to the edge's aircraft capacity.
|
||||
* @return Last update.
|
||||
*/
|
||||
Date LastAircraftUpdate() const { return this->edge.last_aircraft_update; }
|
||||
Date LastAircraftUpdate() const { return this->edge->last_aircraft_update; }
|
||||
|
||||
/**
|
||||
* Get the date of the last update to any part of the edge's capacity.
|
||||
* @return Last update.
|
||||
*/
|
||||
Date LastUpdate() const { return std::max(this->edge.last_unrestricted_update, this->edge.last_restricted_update); }
|
||||
Date LastUpdate() const { return std::max(this->edge->last_unrestricted_update, this->edge->last_restricted_update); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper for a node (const or not) allowing retrieval, but no modification.
|
||||
* @tparam Tedge Actual node class, may be "const BaseNode" or just "BaseNode".
|
||||
* @tparam Tedge Actual edge class, may be "const BaseEdge" or just "BaseEdge".
|
||||
*/
|
||||
template<typename Tnode, typename Tedge>
|
||||
template<typename Tnode>
|
||||
class NodeWrapper {
|
||||
protected:
|
||||
Tnode &node; ///< Node being wrapped.
|
||||
Tedge *edges; ///< Outgoing edges for wrapped node.
|
||||
NodeID index; ///< ID of wrapped node.
|
||||
Tnode &node; ///< Node being wrapped.
|
||||
NodeID index; ///< ID of wrapped node.
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Wrap a node.
|
||||
* @param node Node to be wrapped.
|
||||
* @param edges Outgoing edges for node to be wrapped.
|
||||
* @param index ID of node to be wrapped.
|
||||
*/
|
||||
NodeWrapper(Tnode &node, Tedge *edges, NodeID index) : node(node),
|
||||
edges(edges), index(index) {}
|
||||
NodeWrapper(Tnode &node, NodeID index) : node(node), index(index) {}
|
||||
|
||||
/**
|
||||
* Get supply of wrapped node.
|
||||
@@ -190,117 +200,8 @@ public:
|
||||
* @return Location of the station.
|
||||
*/
|
||||
TileIndex XY() const { return this->node.xy; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for iterating across outgoing edges of a node. Only the real
|
||||
* edges (those with capacity) are iterated. The ones with only distance
|
||||
* information are skipped.
|
||||
* @tparam Tedge Actual edge class. May be "BaseEdge" or "const BaseEdge".
|
||||
* @tparam Titer Actual iterator class.
|
||||
*/
|
||||
template <class Tedge, class Tedge_wrapper, class Titer>
|
||||
class BaseEdgeIterator {
|
||||
protected:
|
||||
Tedge *base; ///< Array of edges being iterated.
|
||||
NodeID current; ///< Current offset in edges array.
|
||||
|
||||
/**
|
||||
* A "fake" pointer to enable operator-> on temporaries. As the objects
|
||||
* returned from operator* aren't references but real objects, we have
|
||||
* to return something that implements operator->, but isn't a pointer
|
||||
* from operator->. A fake pointer.
|
||||
*/
|
||||
class FakePointer : public std::pair<NodeID, Tedge_wrapper> {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a fake pointer from a pair of NodeID and edge.
|
||||
* @param pair Pair to be "pointed" to (in fact shallow-copied).
|
||||
*/
|
||||
FakePointer(const std::pair<NodeID, Tedge_wrapper> &pair) : std::pair<NodeID, Tedge_wrapper>(pair) {}
|
||||
|
||||
/**
|
||||
* Retrieve the pair by operator->.
|
||||
* @return Pair being "pointed" to.
|
||||
*/
|
||||
std::pair<NodeID, Tedge_wrapper> *operator->() { return this; }
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param base Array of edges to be iterated.
|
||||
* @param current ID of current node (to locate the first edge).
|
||||
*/
|
||||
BaseEdgeIterator (Tedge *base, NodeID current) :
|
||||
base(base),
|
||||
current(current == INVALID_NODE ? current : base[current].next_edge)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Prefix-increment.
|
||||
* @return This.
|
||||
*/
|
||||
Titer &operator++()
|
||||
{
|
||||
this->current = this->base[this->current].next_edge;
|
||||
return static_cast<Titer &>(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Postfix-increment.
|
||||
* @return Version of this before increment.
|
||||
*/
|
||||
Titer operator++(int)
|
||||
{
|
||||
Titer ret(static_cast<Titer &>(*this));
|
||||
this->current = this->base[this->current].next_edge;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare with some other edge iterator. The other one may be of a
|
||||
* child class.
|
||||
* @tparam Tother Class of other iterator.
|
||||
* @param other Instance of other iterator.
|
||||
* @return If the iterators have the same edge array and current node.
|
||||
*/
|
||||
template<class Tother>
|
||||
bool operator==(const Tother &other)
|
||||
{
|
||||
return this->base == other.base && this->current == other.current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare for inequality with some other edge iterator. The other one
|
||||
* may be of a child class.
|
||||
* @tparam Tother Class of other iterator.
|
||||
* @param other Instance of other iterator.
|
||||
* @return If either the edge arrays or the current nodes differ.
|
||||
*/
|
||||
template<class Tother>
|
||||
bool operator!=(const Tother &other)
|
||||
{
|
||||
return this->base != other.base || this->current != other.current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dereference with operator*.
|
||||
* @return Pair of current target NodeID and edge object.
|
||||
*/
|
||||
std::pair<NodeID, Tedge_wrapper> operator*() const
|
||||
{
|
||||
return std::pair<NodeID, Tedge_wrapper>(this->current, Tedge_wrapper(this->base[this->current]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dereference with operator->.
|
||||
* @return Fake pointer to Pair of current target NodeID and edge object.
|
||||
*/
|
||||
FakePointer operator->() const {
|
||||
return FakePointer(this->operator*());
|
||||
}
|
||||
NodeID GetNodeID() const { return this->index; }
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -319,46 +220,16 @@ public:
|
||||
*/
|
||||
Edge(BaseEdge &edge) : EdgeWrapper<BaseEdge>(edge) {}
|
||||
void Update(uint capacity, uint usage, uint32 time, EdgeUpdateMode mode);
|
||||
void Restrict() { this->edge.last_unrestricted_update = INVALID_DATE; }
|
||||
void Release() { this->edge.last_restricted_update = INVALID_DATE; }
|
||||
void ClearAircraft() { this->edge.last_aircraft_update = INVALID_DATE; }
|
||||
};
|
||||
|
||||
/**
|
||||
* An iterator for const edges. Cannot be typedef'ed because of
|
||||
* template-reference to ConstEdgeIterator itself.
|
||||
*/
|
||||
class ConstEdgeIterator : public BaseEdgeIterator<const BaseEdge, ConstEdge, ConstEdgeIterator> {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param edges Array of edges to be iterated over.
|
||||
* @param current ID of current edge's end node.
|
||||
*/
|
||||
ConstEdgeIterator(const BaseEdge *edges, NodeID current) :
|
||||
BaseEdgeIterator<const BaseEdge, ConstEdge, ConstEdgeIterator>(edges, current) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* An iterator for non-const edges. Cannot be typedef'ed because of
|
||||
* template-reference to EdgeIterator itself.
|
||||
*/
|
||||
class EdgeIterator : public BaseEdgeIterator<BaseEdge, Edge, EdgeIterator> {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param edges Array of edges to be iterated over.
|
||||
* @param current ID of current edge's end node.
|
||||
*/
|
||||
EdgeIterator(BaseEdge *edges, NodeID current) :
|
||||
BaseEdgeIterator<BaseEdge, Edge, EdgeIterator>(edges, current) {}
|
||||
void Restrict() { this->edge->last_unrestricted_update = INVALID_DATE; }
|
||||
void Release() { this->edge->last_restricted_update = INVALID_DATE; }
|
||||
void ClearAircraft() { this->edge->last_aircraft_update = INVALID_DATE; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Constant node class. Only retrieval operations are allowed on both the
|
||||
* node itself and its edges.
|
||||
*/
|
||||
class ConstNode : public NodeWrapper<const BaseNode, const BaseEdge> {
|
||||
class ConstNode : public NodeWrapper<const BaseNode> {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -366,34 +237,14 @@ public:
|
||||
* @param node ID of the node.
|
||||
*/
|
||||
ConstNode(const LinkGraph *lg, NodeID node) :
|
||||
NodeWrapper<const BaseNode, const BaseEdge>(lg->nodes[node], lg->edges[node], node)
|
||||
NodeWrapper<const BaseNode>(lg->nodes[node], node)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Get a ConstEdge. This is not a reference as the wrapper objects are
|
||||
* not actually persistent.
|
||||
* @param to ID of end node of edge.
|
||||
* @return Constant edge wrapper.
|
||||
*/
|
||||
ConstEdge operator[](NodeID to) const { return ConstEdge(this->edges[to]); }
|
||||
|
||||
/**
|
||||
* Get an iterator pointing to the start of the edges array.
|
||||
* @return Constant edge iterator.
|
||||
*/
|
||||
ConstEdgeIterator Begin() const { return ConstEdgeIterator(this->edges, this->index); }
|
||||
|
||||
/**
|
||||
* Get an iterator pointing beyond the end of the edges array.
|
||||
* @return Constant edge iterator.
|
||||
*/
|
||||
ConstEdgeIterator End() const { return ConstEdgeIterator(this->edges, INVALID_NODE); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Updatable node class. The node itself as well as its edges can be modified.
|
||||
*/
|
||||
class Node : public NodeWrapper<BaseNode, BaseEdge> {
|
||||
class Node : public NodeWrapper<BaseNode> {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -401,29 +252,9 @@ public:
|
||||
* @param node ID of the node.
|
||||
*/
|
||||
Node(LinkGraph *lg, NodeID node) :
|
||||
NodeWrapper<BaseNode, BaseEdge>(lg->nodes[node], lg->edges[node], node)
|
||||
NodeWrapper<BaseNode>(lg->nodes[node], node)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Get an Edge. This is not a reference as the wrapper objects are not
|
||||
* actually persistent.
|
||||
* @param to ID of end node of edge.
|
||||
* @return Edge wrapper.
|
||||
*/
|
||||
Edge operator[](NodeID to) { return Edge(this->edges[to]); }
|
||||
|
||||
/**
|
||||
* Get an iterator pointing to the start of the edges array.
|
||||
* @return Edge iterator.
|
||||
*/
|
||||
EdgeIterator Begin() { return EdgeIterator(this->edges, this->index); }
|
||||
|
||||
/**
|
||||
* Get an iterator pointing beyond the end of the edges array.
|
||||
* @return Constant edge iterator.
|
||||
*/
|
||||
EdgeIterator End() { return EdgeIterator(this->edges, INVALID_NODE); }
|
||||
|
||||
/**
|
||||
* Update the node's supply and set last_update to the current date.
|
||||
* @param supply Supply to be added.
|
||||
@@ -451,15 +282,8 @@ public:
|
||||
{
|
||||
this->node.demand = demand;
|
||||
}
|
||||
|
||||
void AddEdge(NodeID to, uint capacity, uint usage, uint32 time, EdgeUpdateMode mode);
|
||||
void UpdateEdge(NodeID to, uint capacity, uint usage, uint32 time, EdgeUpdateMode mode);
|
||||
void RemoveEdge(NodeID to);
|
||||
};
|
||||
|
||||
typedef std::vector<BaseNode> NodeVector;
|
||||
typedef SmallMatrix<BaseEdge> EdgeMatrix;
|
||||
|
||||
/** Minimum effective distance for timeout calculation. */
|
||||
static const uint MIN_TIMEOUT_DISTANCE = 32;
|
||||
|
||||
@@ -548,6 +372,9 @@ public:
|
||||
NodeID AddNode(const Station *st);
|
||||
void RemoveNode(NodeID id);
|
||||
|
||||
void UpdateEdge(NodeID from, NodeID to, uint capacity, uint usage, uint32 time, EdgeUpdateMode mode);
|
||||
void RemoveEdge(NodeID from, NodeID to);
|
||||
|
||||
inline uint64 CalculateCostEstimate() const {
|
||||
uint64 size_squared = this->Size() * this->Size();
|
||||
return size_squared * FindLastBit(size_squared * size_squared); // N^2 * 4log_2(N)
|
||||
@@ -570,6 +397,89 @@ protected:
|
||||
Date last_compression; ///< Last time the capacities and supplies were compressed.
|
||||
NodeVector nodes; ///< Nodes in the component.
|
||||
EdgeMatrix edges; ///< Edges in the component.
|
||||
|
||||
public:
|
||||
const EdgeMatrix &GetEdges() const { return this->edges; }
|
||||
|
||||
const BaseEdge &GetBaseEdge(NodeID from, NodeID to) const
|
||||
{
|
||||
auto iter = this->edges.find(std::make_pair(from, to));
|
||||
if (iter != this->edges.end()) return iter->second;
|
||||
|
||||
static LinkGraph::BaseEdge empty_edge = {};
|
||||
return empty_edge;
|
||||
}
|
||||
|
||||
ConstEdge GetConstEdge(NodeID from, NodeID to) const { return ConstEdge(this->GetBaseEdge(from, to)); }
|
||||
|
||||
template <typename F>
|
||||
void IterateEdgesFromNode(NodeID from_id, F proc) const
|
||||
{
|
||||
auto iter = this->edges.lower_bound(std::make_pair(from_id, (NodeID)0));
|
||||
while (iter != this->edges.end()) {
|
||||
NodeID from = iter->first.first;
|
||||
NodeID to = iter->first.second;
|
||||
if (from != from_id) return;
|
||||
if (from != to) {
|
||||
proc(from, to, ConstEdge(iter->second));
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
enum class EdgeIterationResult {
|
||||
None,
|
||||
EraseEdge,
|
||||
};
|
||||
|
||||
struct EdgeIterationHelper {
|
||||
EdgeMatrix &edges;
|
||||
EdgeMatrix::iterator &iter;
|
||||
const NodeID from_id;
|
||||
const NodeID to_id;
|
||||
size_t expected_size;
|
||||
|
||||
EdgeIterationHelper(EdgeMatrix &edges, EdgeMatrix::iterator &iter, NodeID from_id, NodeID to_id) :
|
||||
edges(edges), iter(iter), from_id(from_id), to_id(to_id), expected_size(0) {}
|
||||
|
||||
Edge GetEdge() { return Edge(this->iter->second); }
|
||||
|
||||
void RecordSize() { this->expected_size = this->edges.size(); }
|
||||
|
||||
bool RefreshIterationIfSizeChanged()
|
||||
{
|
||||
if (this->expected_size != this->edges.size()) {
|
||||
/* Edges container has resized, our iterator is now invalid, so find it again */
|
||||
this->iter = this->edges.find(std::make_pair(this->from_id, this->to_id));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
void MutableIterateEdgesFromNode(NodeID from_id, F proc)
|
||||
{
|
||||
EdgeMatrix::iterator iter = this->edges.lower_bound(std::make_pair(from_id, (NodeID)0));
|
||||
while (iter != this->edges.end()) {
|
||||
NodeID from = iter->first.first;
|
||||
NodeID to = iter->first.second;
|
||||
if (from != from_id) return;
|
||||
EdgeIterationResult result = EdgeIterationResult::None;
|
||||
if (from != to) {
|
||||
result = proc(EdgeIterationHelper(this->edges, iter, from, to));
|
||||
}
|
||||
switch (result) {
|
||||
case EdgeIterationResult::None:
|
||||
++iter;
|
||||
break;
|
||||
case EdgeIterationResult::EraseEdge:
|
||||
iter = this->edges.erase(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* LINKGRAPH_H */
|
||||
|
Reference in New Issue
Block a user