• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

benchmarks/H03-May-2022-496418

doc/H03-May-2022-44

src/gtpo/H07-Jun-2021-4,2071,992

tests/H07-Jun-2021-2,1191,341

.gitignoreH A D07-Jun-202143 43

GTpo.proH A D07-Jun-2021566 2719

README.mdH A D07-Jun-202112.5 KiB289201

gtpo.priH A D07-Jun-20211.4 KiB3327

README.md

1GTpo  (C++14 Topology library)
2===========================
3
4:heavy_exclamation_mark: **This page is WIP, GTpo is used internally in QuickQanava and is not ready for general use** :heavy_exclamation_mark:
5
6GTpo is a C++14 directed graphs modelling library available under BSD license. GTpo is highly configurable at compile time, with no runtime cost for unconfigured features.
7
8
9+ Project homepage: https://github.com/cneben/QuickQanava/tree/master/GTpo
10
11![GTpo data model schema](https://github.com/cneben/QuickQanava/blob/master/GTpo/doc/gtpo-datamodel.png)
12
13GTpo is **highly** alpha.
14
15- Example use: QuickQanava - Concrete implementation of all GTpo features for QT/QML with complete graph visualization, see: https://github.com/cneben/QuickQanava
16
17**ROADMAP**:
18  - **v0.1.0:**
19    - [X] Push test coverage to 100%.
20    - [ ] Clean the copy and move semantic requirements on topology primitives (and document it).
21    - [ ] Clean operator= and operator== semantic requirements on topology primitives (and document it).
22    - [X] Write complete documentation for static graph configuration.
23    - [ ] Clean management of groups (with regular nodes and specific utility edges...).
24    - [ ] Add complete support for static properties.
25    - [ ] Add generic utilities to ease JSON graph serialization.
26    - [ ] Fork from QuickQanava, release in a dedicated GH repository.
27  - **v0.2.x:**
28    - [ ] Add a naive chinese whispers clustering algorithm (See [Wikipedia description](https://en.wikipedia.org/wiki/Chinese_Whispers_(clustering_method)) and [Dlib implementation](http://dlib.net/dlib/clustering/chinese_whispers_abstract.h.html)).
29  - **v0.4.x:**
30    - [ ] Add basic naive (iterative) support for major social network metrics (HITS, PR, etc.)
31
32Installation
33------------------
34
35GTpo is header only, and can be used with either _qmake_ or _CMake_.
36
37The recommended way of using GTpo is to statically integrate the library as a GIT submodule in your own project. GTpo could then be included directly in your main
38qmake .pro file:
39
40~~~~~~~~~~~~~{.cpp}
41# project main .pro qmake configuration file
42include(./GTpo/src/gtpo.pri)
43~~~~~~~~~~~~~
44
45### Dependencies
46
47* **googletest / googlemock** (only for building tests): https://github.com/google/googletest/
48
49Data model
50------------------
51
52### Adjacency lists
53
54![GTpo data model schema](https://github.com/cneben/QuickQanava/blob/master/GTpo/doc/gtpo-datamodel.png)
55
56  Memory in GTpo is managed with `std::shared_ptr` and `std::weak_ptr`, using/typedef definitions in graph types are prefixed with either *shared_* or *weak_* according
57to the underlying concrete container.
58
59Static graph configuration
60-------------
61
62GTpo use "curiously recurring template pattern" [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) to allow complete configuration of graph topology at compile time using a single static configuration definition based on `gtpo::config<>` template.
63
64![GTpo data model schema](https://github.com/cneben/QuickQanava/blob/master/GTpo/doc/gtpo-datamodel-static_configuration.png)
65
66Available options:
67
68- `gtpo::config::graph_base`: Base class for `gtpo::graph<>`.
69- `gtpo::config::node_base`: Base class for `gtpo::node<>`.
70- `gtpo::config::edge_base`: Base class for `gtpo::edge<>`.
71- `gtpo::config::group_base`: Base class for `gtpo::group<>`.
72
73- `gtpo::config::final_node_t`: Final concrete node type.
74- `gtpo::config::final_edge_t`: Final concrete edge type.
75- `gtpo::config::final_group_t`: Final concrete group type.
76
77- `gtpo::config::container_adapter`: Generic adapter to insert and remove item in a container.
78- `gtpo::config::node_container_t`: Main node container (default to `std::vector`).
79- `gtpo::config::edge_container_t`: Main node container (default to `std::vector`).
80- `gtpo::config::search_container_t`: Container with an `insert()` and `find()` interface used when fast lookup is necessary, mainly used for internal edge caches (default to `std::unordered_set`).
81
82All options have working default values, for example it is usually not necessary to redefine `container_adapter`, since there is support for all standard and Qt containers.
83
84### Behaviours
85
86  Behaviour in GTpo are the preferred way to observe changes in graph topology. Behaviours could be either *static* or *dynamic*, static behaviours are defined at compile time by modifying `gtpo::config` definition, wheras dynamic behaviours could be added or removed at runtime (with a virtual cost).
87
88+ **Static behaviours**: Static behaviour
89
90+ **Dynamic behaviours**: FIXME.
91
92![GTpo data model schema](https://github.com/cneben/QuickQanava/blob/master/GTpo/doc/gtpo-behaviours-class.png)
93
94
95 Behaviours could be disabled by calling `gtpo::behaviour<>::disable()` method, disabling all behaviours might be usefull before calling `gtpo::Graph<>::clear()` method or before serializing the graph in or out.
96
97
98Topology
99-------------
100
101### Iterators (`gtpo/algorithm.h`)
102
103Header: [<gtpo/algorithm.h>](https://github.com/cneben/QuickQanava/blob/master/GTpo/src/algorithm.h)
104
105Graph nodes could be traversed with c++11 range for loops (usually in node insertion order, but it may vary following what containers have been defined in user static graph configuration):
106
107```cpp
108#include <GTpo>
109// ...
110gtpo::graph<> g;
111g.insert_node();
112for (const auto n : g) { /* ... */ }
113// Or
114for (auto n : g) { /* ... */ }
115```
116
117GTpo also offer a DFS iterator to allow inspection of a graph with DFS ordered nodes (with no temporaries):
118```cpp
119gtpo::graph<> g;
120auto n = g.create_node();
121
122// Iterating in DFS order in a one vertex graph:
123auto it = gtpo::begin_dfs(g);
124auto ni = *it;
125  // With: ni.lock().get() == n.lock().get())
126auto it_end = gtpo::end_dfs(g);
127
128// Or with for loop:
129for ( auto it = gtpo::begin_dfs(g); it != gtpo::end_dfs(g); it++ ) {
130    // Do something with *it, which is a gtpo::graph_t::weak_node_t (ie a weak_ptr on a concreate shared_ptr node).
131}
132```
133
134### Traversal algorithms (`gtpo/algorithm.h`)
135
136Header: [<gtpo/algorithm.h>](https://github.com/cneben/QuickQanava/blob/master/GTpo/src/algorithm.h)
137
138**Note:** _all_ recursive functions are postfixed with *'_rec'*, there is no overflow protection for large deep graphs. Alternative non recursive stack based implementation are available for most functions (no postfix).
139
140- `gtpo::depth_rec()`: Return graph depth.
141- `gtpo::linearize_dfs_rec()`: Return a vector of graph nodes linearized in DFS order.
142- `gtpo::levelize_dfs_rec()`: Return a vector of graph nodes ordered level by level in DFS order. Returned vector size equal graph depth, with a vector of ordered nodes for each level.
143
144These traversal functions are specialized for trees:
145
146- `gtpo::tree_depth_rec()`: Return tree depth.
147- `gtpo::linearize_tree_dfs()` / `gtpo::linearize_tree_dfs_rec()`: Return a vector of tree nodes linearized in DFS order.
148- `gtpo::levelize_tree_dfs_rec()`: Return a vector of tree nodes ordered level by level in DFS order. Returned vector size equal tree depth, with a vector of ordered nodes for each level.
149
150Calling a *'_tree'* method is often faster, but using it against a non-tree graph might lead to overflow or infinite recursion. Following methods could be used to check specific graph topological properties:
151
152- `gtp::is_dag_rec()` / `gtp::is_dag()`  (O(N)): Return true if graph is a directed acyclic graph. **FIXME 20180815** There is actually a bug in is_dag_rec() implementation, see the failing test in 'gtpo_lgorithm_tests.cpp' and use with care...
153- `gtp::is_tree_rec()` / `gtp::is_tree()` (O(N)): Return true if graph is a tree (or a forest).
154
155### Functionnals (`gtpo/algorithm.h`)
156
157Header: [<gtpo/functional.h>](https://github.com/cneben/QuickQanava/blob/master/GTpo/src/functional.h)
158
159- `gtp::copy()` (O(N)): Copy a source graph to a destination graph.
160  - Signature: `template <> auto copy(const src_graph_t& src, dst_graph_t& dst) -> bool`
161  - Precondition (return false if not met): destination graph must be empty.
162  - Note: source and destination may be of different types.
163  - Limitations (20180815): Groups are not taken into account.
164  - Throw: May throw std::bad_alloc
165
166```cpp
167#include <GTpo>
168#include "GTpo/functional.h"
169// ...
170gtpo::graph<> src, dst;
171auto n1 = src.create_node();
172auto n2 = src.create_node();
173src.create_edge(n1, n2);
174const auto ok = gtpo::copy(src, dst);
175// ok is true.
176// dst.get_node_count() is 2, dst.get_edge_count() is 1, src and dst are isomorph.
177```
178
179- `gtp::filter()`: Apply a user defined unary functor to filter nodes from a source graph before copying the subset of source graph topology to a destination graph.
180  - Signature: `template <> auto filter(const src_graph_t& src, dst_graph_t& dst, filter_node_func_t f) -> bool`
181  - Precondition (return false if not met): destination graph must be empty.
182  - Note: source and destination may be of different types.
183  - Limitations (20180815): Groups are not taken into account.
184  - Throw: May throw std::bad_alloc
185
186```cpp
187#include <GTpo>
188#include "GTpo/functional.h"
189// ...
190gtpo::graph<> src, dst;
191src.create_node();
192const auto f = [](const auto& node) -> bool { return false; };
193auto ok = gtpo::filter<gtpo::graph<>, gtpo::graph<>, decltype(f)>(src, dst, f);
194// dst graph is now a filtered version of src graph
195
196// Modern compilers could usually infer template arguments type, following syntax should be preferred:
197gtpo::graph<> dst2;
198ok = gtpo::filter<>(src, dst2, f);
199```
200
201User could filter a specific set of subnodes, theses nodes adjacent edges will be preserved by filtering:
202
203```cpp
204gtpo::graph<> src, dst;
205auto n1 = src.create_node().lock();
206auto n2 = src.create_node().lock();
207auto n3 = src.create_node();
208
209src.create_edge(n1, n2);
210src.create_edge(n1, n3);
211src.create_edge(n3, n2);
212
213// SOURCE GRAPH:  { [n1, n2, n3], [ n1 -> n2, n1 -> n3, n3 -> n2 ] },
214
215const auto f = [n1, n2](auto& node) -> bool {
216  return node == n1 || node == n2;        // Filtering all nodes but n1 and n2
217};
218gtpo::filter<>(src, dst, f);
219
220// FILTERED DESTINATION GRAPH:  { [n1, n2], [ n1 -> n2 ] },
221```
222
223
224- `gtp::map()`: Map source graph nodes to a destination graph nodes using a factory/transformation functor.
225  - Signature: `template <> auto map(const src_graph_t& src, dst_graph_t& dst, map_node_func_t f) -> bool`
226  - Precondition (return false if not met): destination graph must be empty.
227  - Note: source and destination may be of different types.
228  - Limitations (20180815): Groups are not taken into account.
229  - Throw: May throw std::bad_alloc
230
231```cpp
232gtpo::graph<> src;
233const auto f = [](const auto& src_node) -> gtpo::graph<>::shared_node_t {
234  static_cast<void>(src_node);
235    return std::make_shared<typename gtpo::graph<>::node_t>();
236};
237gtpo::graph<> dst;
238dst.create_node();
239const auto ok = gtpo::map<gtpo::graph<>, gtpo::graph<>, decltype(f)>(src, dst, f);
240```
241
242
243*Help wanted*:
244- Create an "inplace" version for `gtpo::filter`, where input graph is filtered inplace without a copy to a destination graph.
245- Create a version `gtpo::filter` and `gtpo::copy` where destination (or result) graph is returned directly using RVO and not taken as a function argument.
246
247### Generators (`gtpo/generator.h`)
248
249Header: [<gtpo/generator.h>](https://github.com/cneben/QuickQanava/blob/master/GTpo/src/generator.h)
250
251Graph generation functions interface is similar to [NetworkX](https://networkx.github.io/) generators.
252
253- `gtpo::complete_graph()`: Return a fully connected directed graph. Complexity O(n<sup>2</sup>).
254
255- Random GNP graphs:
256  - `gtpo::gnp_random_graph()`: *TBD*. Complexity O(n<sup>2</sup>).
257  - `gtpo::erdos_renyi_graph()`: *TBD*. Complexity O(n<sup>2</sup>).
258
259- `gtpo::complete_tree_rec()`:  *TBD*.
260
261
262### Group topology
263
264Edges and adjacent edges of a group could be searched with `gtpo::group<>::get_edges()` and `gtpo::group<>::get_adjacent_edges()`. Adjacent edge set won't be initialized until the `gtpo::graph_group_adjacent_edges_behaviour` and `gtpo::group_adjacent_edges_behaviour` static behaviour are configured in `gtpo::config`.
265
266For example: edges = **[** *e4*, *e5* **]**  and adjacent_edges = **[** *e2*, *e3*, *e4*, *e5* **]** :
267
268![](https://github.com/cneben/QuickQanava/blob/master/GTpo/doc/gtpo-topo-group_adjacent_edges.png)
269
270Minimum static behaviour configuration to enable group adjacent edge support is the following (enabled in gtpo::default_config):
271
272~~~~~~~~~~~~~{.cpp}
273struct my_config : public gtpo::config<my_config>
274{
275    using graph_behaviours = std::tuple< gtpo::graph_group_adjacent_edges_behaviour<my_config> >;
276    using group_behaviours = std::tuple< gtpo::group_adjacent_edges_behaviour<my_config> >;
277};
278~~~~~~~~~~~~~
279
280
281
282Benchmarks
283-------------
284
285### Specialized tree functions vs general graph functions
286
287![](https://github.com/cneben/QuickQanava/blob/master/GTpo/doc/linearize_dfs_tree.png)
288
289