Skip to content

Commit

Permalink
More on directed graphs
Browse files Browse the repository at this point in the history
  • Loading branch information
ifsmirnov committed Oct 28, 2017
1 parent fb7f58f commit 6ff42fc
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 50 deletions.
65 changes: 42 additions & 23 deletions impl/graph_inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ class GraphRandom {
"Number of vertices and edges in the graph must be nonnegative");
checkLargeParameter(n);
return BuilderProxy(Traits(n), [](Traits t) {
ensure(
t.n <= 1 || !t.connected,
"Empty graph on >1 vertices cannot be connected");
Graph g;
if (t.directed) {
g.directed_ = true;
Expand All @@ -108,6 +111,7 @@ class GraphRandom {
Graph g;
if (t.directed) {
g.directed_ = true;
ensure(!t.acyclic, "Cannot generate acyclic cycle");
}
for (int i = 0; i < t.n; ++i) {
g.addEdge(i, (i+1)%t.n);
Expand All @@ -132,9 +136,6 @@ class GraphRandom {

private:
static Graph doRandom(Traits t) {
if (t.directed && t.acyclic) {
return doDag(t);
}
int n = t.n;
int m = t.m;

Expand All @@ -159,15 +160,6 @@ class GraphRandom {
}

auto edgeIsGood = [&usedEdges, t](std::pair<int, int> edge) {
// TODO: move this check to edges generation loop
if (!t.allowLoops && edge.first == edge.second) {
ENSURE(false);
return false;
}
if (!t.directed && edge.first > edge.second) {
ENSURE(false);
std::swap(edge.first, edge.second);
}
if (!t.allowMulti && usedEdges.count(edge)) {
return false;
}
Expand All @@ -194,6 +186,10 @@ class GraphRandom {
"Not enough edges found");

Graph graph;

if (t.directed && t.acyclic) {
makeAcyclic(result);
}
if (t.directed) {
graph.directed_ = true;
}
Expand All @@ -209,19 +205,35 @@ class GraphRandom {
}

static Graph doRandomStretched(Traits t, int elongation, int spread) {
ensure(
!t.directed,
"randomStretched not available for directed graphs");

Tree tree = Tree::randomPrim(t.n, elongation);
Array parents = tree.parents(0);
parents[0] = 0;

Graph graph(tree);

auto treeEdges = tree.edges();
if (t.directed && !t.acyclic) {
for (auto& edge: treeEdges) {
if (rnd.next(2)) {
std::swap(edge.first, edge.second);
}
}
}
std::set<std::pair<int, int>> usedEdges(
treeEdges.begin(), treeEdges.end());

auto edgeIsGood = [&usedEdges, t](std::pair<int, int> edge) {
if (!t.allowMulti && usedEdges.count(edge)) {
return false;
}
if (t.directed && !t.allowAntiparallel &&
usedEdges.count({edge.second, edge.first}))
{
return false;
}
return true;
};

while (graph.m() != t.m) {
int u = rnd.next(t.n);
int up = rnd.next(0, spread);
Expand All @@ -236,10 +248,14 @@ class GraphRandom {
continue;
}

if (!t.allowMulti && usedEdges.count({v, u})) {
if (!edgeIsGood({v, u})) {
continue;
}

if (t.directed && !t.acyclic && rnd.next(2)) {
std::swap(u, v);
}

graph.addEdge(u, v);
usedEdges.emplace(u, v);
}
Expand All @@ -248,12 +264,6 @@ class GraphRandom {
return graph;
}

static Graph doDag(Traits t) {
(void)t;
ENSURE(false, "No dags at the moment");

}

static std::pair<int, int> randomEdge(int n, const Traits& t) {
return rnd.nextp(n, RandomPairTraits{!t.directed, !t.allowLoops});
}
Expand All @@ -269,6 +279,15 @@ class GraphRandom {
}
return res;
}

static void makeAcyclic(Arrayp& edges) {
auto numbering = Array::id(edges.size()).shuffle();
for (auto& edge: edges) {
if (numbering[edge.first] > numbering[edge.second]) {
std::swap(edge.first, edge.second);
}
}
}
};

} // namespace graph_detail
Expand Down
65 changes: 42 additions & 23 deletions jngen.h
Original file line number Diff line number Diff line change
Expand Up @@ -6164,6 +6164,9 @@ class GraphRandom {
"Number of vertices and edges in the graph must be nonnegative");
checkLargeParameter(n);
return BuilderProxy(Traits(n), [](Traits t) {
ensure(
t.n <= 1 || !t.connected,
"Empty graph on >1 vertices cannot be connected");
Graph g;
if (t.directed) {
g.directed_ = true;
Expand All @@ -6182,6 +6185,7 @@ class GraphRandom {
Graph g;
if (t.directed) {
g.directed_ = true;
ensure(!t.acyclic, "Cannot generate acyclic cycle");
}
for (int i = 0; i < t.n; ++i) {
g.addEdge(i, (i+1)%t.n);
Expand All @@ -6206,9 +6210,6 @@ class GraphRandom {

private:
static Graph doRandom(Traits t) {
if (t.directed && t.acyclic) {
return doDag(t);
}
int n = t.n;
int m = t.m;

Expand All @@ -6233,15 +6234,6 @@ class GraphRandom {
}

auto edgeIsGood = [&usedEdges, t](std::pair<int, int> edge) {
// TODO: move this check to edges generation loop
if (!t.allowLoops && edge.first == edge.second) {
ENSURE(false);
return false;
}
if (!t.directed && edge.first > edge.second) {
ENSURE(false);
std::swap(edge.first, edge.second);
}
if (!t.allowMulti && usedEdges.count(edge)) {
return false;
}
Expand All @@ -6268,6 +6260,10 @@ class GraphRandom {
"Not enough edges found");

Graph graph;

if (t.directed && t.acyclic) {
makeAcyclic(result);
}
if (t.directed) {
graph.directed_ = true;
}
Expand All @@ -6283,19 +6279,35 @@ class GraphRandom {
}

static Graph doRandomStretched(Traits t, int elongation, int spread) {
ensure(
!t.directed,
"randomStretched not available for directed graphs");

Tree tree = Tree::randomPrim(t.n, elongation);
Array parents = tree.parents(0);
parents[0] = 0;

Graph graph(tree);

auto treeEdges = tree.edges();
if (t.directed && !t.acyclic) {
for (auto& edge: treeEdges) {
if (rnd.next(2)) {
std::swap(edge.first, edge.second);
}
}
}
std::set<std::pair<int, int>> usedEdges(
treeEdges.begin(), treeEdges.end());

auto edgeIsGood = [&usedEdges, t](std::pair<int, int> edge) {
if (!t.allowMulti && usedEdges.count(edge)) {
return false;
}
if (t.directed && !t.allowAntiparallel &&
usedEdges.count({edge.second, edge.first}))
{
return false;
}
return true;
};

while (graph.m() != t.m) {
int u = rnd.next(t.n);
int up = rnd.next(0, spread);
Expand All @@ -6310,10 +6322,14 @@ class GraphRandom {
continue;
}

if (!t.allowMulti && usedEdges.count({v, u})) {
if (!edgeIsGood({v, u})) {
continue;
}

if (t.directed && !t.acyclic && rnd.next(2)) {
std::swap(u, v);
}

graph.addEdge(u, v);
usedEdges.emplace(u, v);
}
Expand All @@ -6322,12 +6338,6 @@ class GraphRandom {
return graph;
}

static Graph doDag(Traits t) {
(void)t;
ENSURE(false, "No dags at the moment");

}

static std::pair<int, int> randomEdge(int n, const Traits& t) {
return rnd.nextp(n, RandomPairTraits{!t.directed, !t.allowLoops});
}
Expand All @@ -6343,6 +6353,15 @@ class GraphRandom {
}
return res;
}

static void makeAcyclic(Arrayp& edges) {
auto numbering = Array::id(edges.size()).shuffle();
for (auto& edge: edges) {
if (numbering[edge.first] > numbering[edge.second]) {
std::swap(edge.first, edge.second);
}
}
}
};

} // namespace graph_detail
Expand Down
59 changes: 55 additions & 4 deletions tests/graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ BOOST_AUTO_TEST_CASE(output) {
std::ostringstream ss;
ss << g.printN().printM().add1() << std::endl;

BOOST_CHECK_EQUAL(ss.str(), "4 6\n1 3\n2 4\n1 4\n1 4\n2 3\n3 2\n");
BOOST_TEST(ss.str() == "4 6\n1 3\n2 4\n1 4\n1 4\n2 3\n3 2\n");
}

BOOST_AUTO_TEST_CASE(weights_and_labelling) {
Expand All @@ -40,8 +40,8 @@ BOOST_AUTO_TEST_CASE(weights_and_labelling) {
int v5 = -1, v8 = -1;
std::string s;
ss >> v1 >> v2;
BOOST_CHECK_EQUAL(v1, g.n());
BOOST_CHECK_EQUAL(v2, g.m());
BOOST_TEST(v1 == g.n());
BOOST_TEST(v2 == g.m());

for (int i = 0; i < g.n(); ++i) {
ss >> s;
Expand All @@ -63,7 +63,58 @@ BOOST_AUTO_TEST_CASE(weights_and_labelling) {
}
}

BOOST_CHECK_EQUAL(count, 1);
BOOST_TEST(count == 1);
}

template<typename T>
void generateWithTraitsMask(T&& generator, const std::string& name, int mask) {
if (mask&(1<<0)) generator.allowAntiparallel();
if (mask&(1<<1)) generator.allowLoops();
if (mask&(1<<2)) generator.allowMulti();
if (mask&(1<<3)) generator.connected();
if (mask&(1<<4)) generator.directed();
if (mask&(1<<5)) generator.acyclic();
try {
generator.g();
} catch (jngen::Exception) {
// directed acyclic cycle
if (name == "cycle" && (mask & ((1<<4) | (1<<5)))) {
return;
}

// connected empty graph
if (name == "empty" && (mask & (1<<3))) {
return;
}

throw;

/*
// left here for debug purposes
std::cerr << name << ": ";
for (int i = 0; i < 6; ++i) {
if (mask&(1<<i)) {
std::cerr << "+";
} else {
std::cerr << "-";
}
}
std::cerr << std::endl;
*/
}
}

BOOST_AUTO_TEST_CASE(various_traits) {
BOOST_CHECK(true);

for (int mask = 0; mask < (1<<6); ++mask) {
generateWithTraitsMask(Graph::random(10, 15), "random", mask);
generateWithTraitsMask(
Graph::randomStretched(10, 15, 5, 5), "randomStretched", mask);
generateWithTraitsMask(Graph::complete(10), "complete", mask);
generateWithTraitsMask(Graph::cycle(10), "cycle", mask);
generateWithTraitsMask(Graph::empty(10), "empty", mask);
}
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 6ff42fc

Please sign in to comment.