Skip to content

Commit

Permalink
plugins/import: Improve Random General Tree implementation
Browse files Browse the repository at this point in the history
Use an implementation inspired from the randomTree function from
OGDF instead of the legacy Tulip one which is not optimal at all in
terms of performance.

Ensure max size parameter is honored.

Add tests.
  • Loading branch information
anlambert committed Nov 14, 2023
1 parent 463415f commit 964d3b6
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 50 deletions.
73 changes: 23 additions & 50 deletions plugins/import/RandomTreeGeneral.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
*
* Copyright (C) 2019-2022 The Talipot developers
* Copyright (C) 2019-2023 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 @@ -29,43 +29,19 @@ static constexpr std::string_view paramHelp[] = {
// tree layout
"If true, the generated tree is drawn with the 'Tree Leaf' layout algorithm."};

/// Random General Tree - Import of a random general tree
/** This plugin enables to create a random general tree
/**
* This plugin enables to create a random general tree.
*
* User can specify the minimal/maximal number of nodes and the maximal degree.
*
* The implementation is freely inspired from the randomTree function implemented
* in OGDF.
*/
class RandomTreeGeneral : public ImportModule {

bool buildNode(node n, uint sizeM, int arityMax) {
if (graph->numberOfNodes() >= sizeM) {
return true;
}

bool result = true;
int randNumber = randomInteger(RAND_MAX);
int i = 0;

while (randNumber < RAND_MAX / pow(2.0, 1.0 + i)) {
++i;
}

i = i % arityMax;
graph->reserveNodes(i);
graph->reserveEdges(i);

for (; i > 0; --i) {
node n1;
n1 = graph->addNode();
graph->addEdge(n, n1);
result = result && buildNode(n1, sizeM, arityMax);
}

return result;
}

public:
PLUGININFORMATION("Random General Tree", "Auber", "16/02/2001",
"Imports a new randomly generated tree.", "1.1", "Graph")
"Imports a new randomly generated tree.", "2.0", "Graph")
RandomTreeGeneral(tlp::PluginContext *context) : ImportModule(context) {
addInParameter<unsigned>("Minimum size", paramHelp[0].data(), "10");
addInParameter<unsigned>("Maximum size", paramHelp[1].data(), "100");
Expand Down Expand Up @@ -124,31 +100,28 @@ class RandomTreeGeneral : public ImportModule {
return false;
}

bool ok = true;
int i = 0;
uint nbTest = 0;
graph->clear();

while (ok) {
++nbTest;
uint n = sizeMin + randomUnsignedInteger(sizeMax - sizeMin);

if (nbTest % 100 == 0) {
if (pluginProgress->progress((i / 100) % 100, 100) != ProgressState::TLP_CONTINUE) {
break;
}
}
uint max = 0;
vector<node> possible(n);
possible[0] = graph->addNode();
--n;

++i;
graph->clear();
node n = graph->addNode();
ok = !buildNode(n, sizeMax, arityMax);
while (n > 0) {
uint i = randomUnsignedInteger(max);
node v = possible[i];

if (graph->numberOfNodes() < sizeMin) {
ok = true;
if (v.isValid() && graph->outdeg(v) + 1 == arityMax) {
possible[i] = possible[max--];
}
}

if (pluginProgress->progress(100, 100) == ProgressState::TLP_CANCEL) {
return false;
node w = graph->addNode();
possible[++max] = w;
graph->addEdge(v, w);

--n;
}

if (needLayout) {
Expand Down
21 changes: 21 additions & 0 deletions tests/python/test_talipot_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,24 @@ def test_alpha_mapping(self):

for e in graph.edges():
self.assertEqual(colorProp[e], edgeColorAfter)

def random_general_tree_test(self, min_size, max_size, max_degree):
plugin_name = "Random General Tree"
params = tlp.getDefaultPluginParameters(plugin_name)
params["Minimum size"] = min_size
params["Maximum size"] = max_size
params["Maximal node's degree"] = max_degree

graph = tlp.importGraph(plugin_name, params)

self.assertTrue(graph.numberOfNodes() == graph.numberOfEdges() + 1)
self.assertTrue(graph.numberOfNodes() >= min_size)
self.assertTrue(graph.numberOfNodes() <= max_size)

self.assertTrue(all(graph.outdeg(n) <= max_degree for n in graph.nodes()))

def test_random_general_tree(self):
self.random_general_tree_test(min_size=1000, max_size=2000, max_degree=20)

def test_random_general_tree_fixed_size(self):
self.random_general_tree_test(min_size=1000, max_size=1000, max_degree=5)

0 comments on commit 964d3b6

Please sign in to comment.