Skip to content

Commit

Permalink
talipot-core/GraphImpl: Simplify node/edge iterators implementation
Browse files Browse the repository at this point in the history
Move iterators implementation from GraphStorage to GraphImpl and use
iterator helpers to reimplement them.
  • Loading branch information
anlambert committed Dec 17, 2024
1 parent febd18e commit 073001a
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 213 deletions.
48 changes: 2 additions & 46 deletions library/talipot-core/include/talipot/GraphStorage.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
*
* Copyright (C) 2019-2021 The Talipot developers
* Copyright (C) 2019-2024 The Talipot developers
*
* Talipot is a fork of Tulip, created by David Auber
* and the Tulip development Team from LaBRI, University of Bordeaux
Expand Down Expand Up @@ -124,33 +124,7 @@ class GraphStorage {
* @brief restore a state of the ids management
*/
void restoreIdsMemento(const GraphStorageIdsMemento *);
//=======================================================
/**
* @brief Return a Talipot iterator on edges of the graph
* @warning: The returned iterator should be deleted by the caller to prevent memory leaks
*/
Iterator<edge> *getEdges() const {
return edgeIds.getElts();
}
//=======================================================
/**
* @brief Return a Talipot iterator on adjacent edges of the node n
* @warning: be careful that loops appear twice
* @warning: The returned iterator should be deleted by the caller to prevent memory leaks
*/
Iterator<edge> *getInOutEdges(const node n) const;
//=======================================================
/**
* @brief Return a Talipot iterator on out edges of the node n
* @warning: The returned iterator must be deleted by the caller to prevent memory leaks
*/
Iterator<edge> *getOutEdges(const node n) const;
//=======================================================
/**
* @brief Return a Talipot iterator on in edges of the node n
* @warning: The returned iterator must be deleted by the caller to prevent memory leaks
*/
Iterator<edge> *getInEdges(const node n) const;

//=======================================================
/**
* @brief Return if edges exist between two nodes
Expand All @@ -165,24 +139,6 @@ class GraphStorage {
std::vector<edge> getEdges(const node src, const node tgt, bool directed,
const Graph *sg = nullptr) const;

//=======================================================
/**
* @brief Return a Talipot iterator on adjacent nodes of the node n
* @warning: The returned iterator must be deleted by the caller to prevent memory leaks
*/
Iterator<node> *getInOutNodes(const node n) const;
//=======================================================
/**
* @brief Return a Talipot iterator on in nodes of the node n
* @warning: The returned iterator must be deleted by the caller to prevent memory leaks
*/
Iterator<node> *getInNodes(const node n) const;
//=======================================================
/**
* @brief Return a Talipot iterator on out nodes of the node n
* @warning: The returned iterator must be deleted by the caller to prevent memory leaks
*/
Iterator<node> *getOutNodes(const node n) const;
//=======================================================
/**
* @brief Return the degree of a node
Expand Down
27 changes: 18 additions & 9 deletions library/talipot-core/src/GraphImpl.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
*
* Copyright (C) 2019-2023 The Talipot developers
* Copyright (C) 2019-2024 The Talipot developers
*
* Talipot is a fork of Tulip, created by David Auber
* and the Tulip development Team from LaBRI, University of Bordeaux
Expand All @@ -17,6 +17,7 @@
#include <talipot/PropertyManager.h>
#include <talipot/GraphView.h>
#include <talipot/GraphUpdatesRecorder.h>
#include <talipot/UniqueIterator.h>

using namespace std;
using namespace tlp;
Expand Down Expand Up @@ -232,35 +233,43 @@ void GraphImpl::delEdge(const edge e, bool) {
}
//----------------------------------------------------------------
Iterator<node> *GraphImpl::getNodes() const {
return storage.getNodes();
return stlIterator(storage.nodes());
}
//----------------------------------------------------------------
Iterator<node> *GraphImpl::getInNodes(const node n) const {
return storage.getInNodes(n);
return uniqueIterator(conversionIterator<node>(
filterIterator(storage.incidence(n), [this, n](const edge e) { return target(e) == n; }),
[this](const edge e) { return source(e); }));
}
//----------------------------------------------------------------
Iterator<node> *GraphImpl::getOutNodes(const node n) const {
return storage.getOutNodes(n);
return uniqueIterator(conversionIterator<node>(
filterIterator(storage.incidence(n), [this, n](const edge e) { return source(e) == n; }),
[this](const edge e) { return target(e); }));
}
//----------------------------------------------------------------
Iterator<node> *GraphImpl::getInOutNodes(const node n) const {
return storage.getInOutNodes(n);
return conversionIterator<node>(storage.incidence(n), [this, n](const edge e) {
return source(e) == n ? target(e) : source(e);
});
}
//----------------------------------------------------------------
Iterator<edge> *GraphImpl::getEdges() const {
return storage.getEdges();
return stlIterator(storage.edges());
}
//----------------------------------------------------------------
Iterator<edge> *GraphImpl::getInEdges(const node n) const {
return storage.getInEdges(n);
return uniqueIterator(
filterIterator(storage.incidence(n), [this, n](const edge e) { return target(e) == n; }));
}
//----------------------------------------------------------------
Iterator<edge> *GraphImpl::getOutEdges(const node n) const {
return storage.getOutEdges(n);
return uniqueIterator(
filterIterator(storage.incidence(n), [this, n](const edge e) { return source(e) == n; }));
}
//----------------------------------------------------------------
Iterator<edge> *GraphImpl::getInOutEdges(const node n) const {
return storage.getInOutEdges(n);
return stlIterator(storage.incidence(n));
}
//----------------------------------------------------------------
std::vector<edge> GraphImpl::getEdges(const node src, const node tgt, bool directed) const {
Expand Down
155 changes: 1 addition & 154 deletions library/talipot-core/src/GraphStorage.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
*
* Copyright (C) 2019-2023 The Talipot developers
* Copyright (C) 2019-2024 The Talipot developers
*
* Talipot is a fork of Tulip, created by David Auber
* and the Tulip development Team from LaBRI, University of Bordeaux
Expand Down Expand Up @@ -63,139 +63,6 @@ void GraphStorage::restoreIdsMemento(const GraphStorageIdsMemento *memento) {
edgeIds = memento->edgeIds;
}
//=======================================================
// specific iterator classes used to implement
// the get*Nodes & get*Edges methods

// define some values for further template specializations
// IO_IN => in nodes/edges
// IO_OUT => out nodes/edges
// IO_INOUT => inout nodes/edges
// IO_ prefix is needed on windows platform
// to avoid compilation failure
enum IO_TYPE { IO_IN = 0, IO_OUT = 1, IO_INOUT = 2 };

// define a template class to iterate on in or out edges of a given node
template <IO_TYPE io_type>
struct IOEdgeContainerIterator : public Iterator<edge>,
public MemoryPool<IOEdgeContainerIterator<io_type>> {
node n;
edge curEdge;
std::set<edge> loops;
const std::vector<std::pair<node, node>> &edges;
std::vector<edge>::iterator it, itEnd;

void prepareNext() {
for (; it != itEnd; ++it) {
curEdge = *it;
const auto &[curEdgeSrc, curEdgeTgt] = edges[curEdge.id];
// note that io_type value may only be IO_IN which is null
// or IO_OUT which is define to 1
node curNode;

if constexpr (io_type != IO_IN) {
curNode = curEdgeSrc;
} else {
curNode = curEdgeTgt;
}

if (curNode != n) {
continue;
}

if constexpr (io_type != IO_IN) {
curNode = curEdgeTgt;
} else {
curNode = curEdgeSrc;
}

if (curNode == n) {
if (!loops.contains(curEdge)) {
loops.insert(curEdge);
++it;
return;
}
} else {
++it;
return;
}
}

// set curEdge as invalid
curEdge = edge();
}

IOEdgeContainerIterator(node n, std::vector<edge> &v,
const std::vector<std::pair<node, node>> &edges)
: n(n), edges(edges), it(v.begin()), itEnd(v.end()) {
prepareNext();
}

~IOEdgeContainerIterator() override = default;

edge next() override {
// check hasNext()
assert(curEdge.isValid());
// we are already pointing to the next
edge tmp = curEdge;
// anticipating the next iteration
prepareNext();
return tmp;
}

bool hasNext() override {
return (curEdge.isValid());
}
};

// define a class to iterate on in or out nodes of a given node
template <IO_TYPE io_type>
struct IONodesIterator : public Iterator<node>, public MemoryPool<IONodesIterator<io_type>> {
node n;
const std::vector<std::pair<node, node>> &edges;
Iterator<edge> *it;

IONodesIterator(node n, std::vector<edge> &nEdges,
const std::vector<std::pair<node, node>> &edges)
: n(n), edges(edges) {
if constexpr (io_type == IO_INOUT) {
it = stlIterator(nEdges);
} else {
it = new IOEdgeContainerIterator<io_type>(n, nEdges, edges);
}
}

~IONodesIterator() override {
delete it;
}

bool hasNext() override {
return (it->hasNext());
}

node next() override {
// check hasNext()
assert(it->hasNext());
const auto &[src, tgt] = edges[it->next()];

// out nodes
if constexpr (io_type == IO_OUT) {
return tgt;
}
// in nodes
else if constexpr (io_type == IO_IN) {
return src;
}
// inout nodes
else {
return (src == n) ? tgt : src;
}
}
};
//=======================================================
Iterator<edge> *GraphStorage::getInOutEdges(const node n) const {
return stlIterator(nodeData[n.id].edges);
}
//=======================================================
std::vector<edge> GraphStorage::getEdges(const node src, const node tgt, bool directed,
const Graph *sg) const {

Expand All @@ -217,26 +84,6 @@ std::vector<edge> GraphStorage::getEdges(const node src, const node tgt, bool di
return edges;
}
//=======================================================
Iterator<edge> *GraphStorage::getOutEdges(const node n) const {
return new IOEdgeContainerIterator<IO_OUT>(n, nodeData[n.id].edges, edgeEnds);
}
//=======================================================
Iterator<edge> *GraphStorage::getInEdges(const node n) const {
return new IOEdgeContainerIterator<IO_IN>(n, nodeData[n.id].edges, edgeEnds);
}
//=======================================================
Iterator<node> *GraphStorage::getInOutNodes(const node n) const {
return new IONodesIterator<IO_INOUT>(n, nodeData[n.id].edges, edgeEnds);
}
//=======================================================
Iterator<node> *GraphStorage::getInNodes(const node n) const {
return new IONodesIterator<IO_IN>(n, nodeData[n.id].edges, edgeEnds);
}
//=======================================================
Iterator<node> *GraphStorage::getOutNodes(const node n) const {
return new IONodesIterator<IO_OUT>(n, nodeData[n.id].edges, edgeEnds);
}
//=======================================================
/**
* @brief Reconnect the edge e to have the new given ends
*/
Expand Down
8 changes: 4 additions & 4 deletions library/talipot-core/src/Observable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#include <talipot/ConversionIterator.h>
#include <talipot/FilterIterator.h>
#include <talipot/Exception.h>
#include <talipot/GraphStorage.h>
#include <talipot/GraphImpl.h>
#include <talipot/VectorProperty.h>

using namespace std;
Expand All @@ -35,7 +35,7 @@ TLP_DEFINE_GLOBAL_LOCK(ObservableGraphUpdate);
//=================================

static bool oGraphDestroyed = false;
class ObservationGraph : public GraphStorage {
class ObservationGraph : public GraphImpl {
public:
// a pointer to the object represented by a node
tlp::NodeVectorProperty<Observable *> pointer;
Expand Down Expand Up @@ -314,7 +314,7 @@ void Observable::addOnlooker(const Observable &obs, OBSERVABLEEDGETYPE type) con
edge link;

if (isBound() && obs.isBound()) {
auto edges = observationGraph.getEdges(obs._n, _n, false);
auto edges = observationGraph.getEdges(obs._n, _n, false, nullptr);
if (!edges.empty()) {
link = edges[0];
}
Expand Down Expand Up @@ -531,7 +531,7 @@ void Observable::removeOnlooker(const Observable &obs, OBSERVABLEEDGETYPE type)
throw ObservableException("removeOnlooker called on a deleted Observable");
}

auto edges = observationGraph.getEdges(obs._n, _n, false);
auto edges = observationGraph.getEdges(obs._n, _n, false, nullptr);
edge link;
if (!edges.empty()) {
link = edges[0];
Expand Down

0 comments on commit 073001a

Please sign in to comment.