Browse Source

aga,agar: Negative weight cycle testcase

Adds a new test graph which includes a negative weight cycle.  This means
that shortest paths are not well defined, and both Dijkstra's algorithm and
the Bellman-Ford algorithm (which can handle some negative edge weights)
will fail.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
David Gibson 9 years ago
parent
commit
318edb2466

+ 5 - 1
ccan/aga/test/api-adjacency.c

@@ -63,8 +63,9 @@ int main(void)
 	struct traversal1_graph t1g;
 	struct shortcut1_graph s1g;
 	struct shortcut2_graph s2g;
+	struct negacycle_graph ng;
 
-	plan_tests(2 + 7 + 35 + 30 + 30 + 42 + 9 + 30 + 9 + 9);
+	plan_tests(2 + 7 + 35 + 30 + 30 + 42 + 9 + 30 + 9 + 9 + 9);
 
 	trivial_graph_init(&tg);
 	test_adjacency("trivial", &tg.sg, trivial_adjacency);
@@ -99,5 +100,8 @@ int main(void)
 	shortcut2_graph_init(&s2g);
 	test_adjacency("shortcut2 graph", &s2g.sg, shortcut2_adjacency);
 
+	negacycle_graph_init(&ng);
+	test_adjacency("negacycle graph", &ng.sg, negacycle_adjacency);
+
 	return exit_status();
 }

+ 14 - 1
ccan/aga/test/api-bellman_ford.c

@@ -246,12 +246,24 @@ static void test_shortcut2(void)
 	aga_finish(&s2g.sg.g);
 }
 
+static void test_negacycle(void)
+{
+	struct negacycle_graph ng;
+
+	negacycle_graph_init(&ng);
+
+	ok1(aga_bellman_ford_start(&ng.sg.g, &ng.sg.nodes[1]) == 0);
+	aga_bellman_ford_complete(&ng.sg.g);
+	ok1(aga_error(&ng.sg.g) == AGA_ERR_NEGATIVE_COST);
+	aga_finish(&ng.sg.g);
+}
+
 int main(void)
 {
 	plan_tests(5 + 15
 		   + FULL_LEN * (1 + FULL_LEN * 4)
 		   + CHAIN_LEN * (1 + CHAIN_LEN * 2)
-		   + 10 + 32 + 7 + 7);
+		   + 10 + 32 + 7 + 7 + 2);
 
 	test_trivial();
 	test_parallel();
@@ -261,6 +273,7 @@ int main(void)
 	test_traversal1();
 	test_shortcut1();
 	test_shortcut2();
+	test_negacycle();
 	
 	return exit_status();
 }

+ 7 - 1
ccan/aga/test/api-bfs.c

@@ -42,9 +42,10 @@ int main(void)
 	struct grid_graph gg1, gg2;
 	struct error_graph eg;
 	struct traversal1_graph t1g;
+	struct negacycle_graph ng;
 	struct aga_node *node;
 	
-	plan_tests(2 * 13 + 10 + 10);
+	plan_tests(2 * 13 + 10 + 10 + 6);
 
 	trivial_graph_init(&tg);
 	test_bfs(&tg.sg, 1, 1);
@@ -100,5 +101,10 @@ int main(void)
 	test_bfs_partial(&t1g.sg, 1, 1, 2, 3);
 	aga_finish(&t1g.sg.g);
 
+	negacycle_graph_init(&ng);
+	test_bfs(&ng.sg, 1, 1, 2, 3);
+	test_bfs(&ng.sg, 2, 2, 3, 1);
+	test_bfs(&ng.sg, 3, 3, 1, 2);
+	
 	return exit_status();
 }

+ 7 - 1
ccan/aga/test/api-dfs.c

@@ -42,9 +42,10 @@ int main(void)
 	struct grid_graph gg1, gg2;
 	struct error_graph eg;
 	struct traversal1_graph t1g;
+	struct negacycle_graph ng;
 	struct aga_node *node;
 	
-	plan_tests(2 * 13 + 10 + 10);
+	plan_tests(2 * 13 + 10 + 10 + 6);
 
 	trivial_graph_init(&tg);
 	test_dfs(&tg.sg, 1, 1);
@@ -100,5 +101,10 @@ int main(void)
 	test_dfs_partial(&t1g.sg, 1, 1, 2, 3);
 	aga_finish(&t1g.sg.g);
 
+	negacycle_graph_init(&ng);
+	test_dfs(&ng.sg, 1, 1, 2, 3);
+	test_dfs(&ng.sg, 2, 2, 3, 1);
+	test_dfs(&ng.sg, 3, 3, 1, 2);
+
 	return exit_status();
 }

+ 14 - 1
ccan/aga/test/api-dijkstra.c

@@ -246,12 +246,24 @@ static void test_shortcut2(void)
 	aga_finish(&s2g.sg.g);
 }
 
+static void test_negacycle(void)
+{
+	struct negacycle_graph ng;
+
+	negacycle_graph_init(&ng);
+
+	ok1(aga_dijkstra_start(&ng.sg.g, &ng.sg.nodes[1]) == 0);
+	aga_dijkstra_complete(&ng.sg.g);
+	ok1(aga_error(&ng.sg.g) == AGA_ERR_NEGATIVE_COST);
+	aga_finish(&ng.sg.g);
+}
+
 int main(void)
 {
 	plan_tests(7 + 20
 		   + FULL_LEN * (1 + FULL_LEN*4)
 		   + CHAIN_LEN * (1 + CHAIN_LEN*2)
-		   + 12 + 32 + 7 + 2);
+		   + 12 + 32 + 7 + 2 + 2);
 
 	test_trivial();
 	test_parallel();
@@ -261,6 +273,7 @@ int main(void)
 	test_traversal1();
 	test_shortcut1();
 	test_shortcut2();
+	test_negacycle();
 	
 	return exit_status();
 }

+ 46 - 0
ccan/aga/test/negacycle.c

@@ -0,0 +1,46 @@
+#include "config.h"
+
+#include <assert.h>
+
+#include <ccan/container_of/container_of.h>
+#include <ccan/ptrint/ptrint.h>
+
+#include <ccan/aga/aga.h>
+
+#include "simple-graph.h"
+
+static ptrint_t *negacycle_first_edge(const struct aga_graph *g,
+				      const struct aga_node *n)
+{
+	return int2ptr(1);
+}
+
+static ptrint_t *negacycle_next_edge(const struct aga_graph *g,
+				     const struct aga_node *n,
+				     ptrint_t *e)
+{
+	assert(ptr2int(e) == 1);
+	return NULL;
+}
+
+static int negacycle_edge_info(const struct aga_graph *g,
+			       const struct aga_node *n,
+			       ptrint_t *e, struct aga_edge_info *ei)
+{
+	struct negacycle_graph *ng = container_of(g, struct negacycle_graph,
+						   sg.g);
+	int ni = n - ng->sg.nodes;
+
+	assert(ptr2int(e) == 1);
+	ei->to = &ng->sg.nodes[(ni % 3) + 1];
+	if (ni == 3)
+		ei->icost = -3;
+	return 0;
+}
+
+void negacycle_graph_init(struct negacycle_graph *ng)
+{
+	simple_graph_init(&ng->sg, negacycle_first_edge,
+			  negacycle_next_edge,
+			  negacycle_edge_info);
+}

+ 19 - 0
ccan/aga/test/simple-graph.h

@@ -256,4 +256,23 @@ static const struct adjacency_list shortcut2_adjacency[] = {
 	{},
 };
 
+/* Negacycle graph
+ *
+ *  A <---- (-3) ----- C
+ *   \                 ^
+ *    (1)->  B -- (1)-/
+ *
+ * Graph with a negative length cycle, and so lacking well-defined shortest paths.
+ */
+struct negacycle_graph {
+	struct simple_graph sg;
+};
+void negacycle_graph_init(struct negacycle_graph *ng);
+static const struct adjacency_list negacycle_adjacency[] = {
+	{1, {2}},
+	{2, {3}},
+	{3, {1}},
+	{},
+};
+
 #endif /* _TEST_GRAPHS_H */

+ 12 - 1
ccan/agar/test/api-bellman_ford.c

@@ -229,12 +229,22 @@ static void test_shortcut2(void)
 	tal_free(sr);	
 }
 
+static void test_negacycle(void)
+{
+	struct agar_state *sr;
+
+	ok1(sr = agar_bellman_ford_new(NULL, &negacycle_graphr.gr, int2ptr(1)));
+	agar_bellman_ford_complete(sr);
+	ok1(agar_error(sr) == AGA_ERR_NEGATIVE_COST);
+	tal_free(sr);
+}
+
 int main(void)
 {
 	plan_tests(3 + 15
 		   + FULL_LEN * (FULL_LEN*4 - 1)
 		   + CHAIN_LEN * (1 + CHAIN_LEN*2)
-		   + 10 + 32 + 7 + 7);
+		   + 10 + 32 + 7 + 7 + 2);
 
 	test_trivial();
 	test_parallel();
@@ -244,6 +254,7 @@ int main(void)
 	test_traversal1();
 	test_shortcut1();
 	test_shortcut2();
+	test_negacycle();
 	
 	return exit_status();
 }

+ 5 - 1
ccan/agar/test/api-bfs.c

@@ -45,7 +45,7 @@ int main(void)
 	struct agar_state *sr;
 	const void *nr;
 	
-	plan_tests(2 * 13 + 12 + 10);
+	plan_tests(2 * 13 + 12 + 10 + 6);
 
 	test_bfs(&trivial_graphr.gr, 1, 1);
 
@@ -97,5 +97,9 @@ int main(void)
 	test_bfs_partial(sr, 1, 1, 2, 3);
 	tal_free(sr);
 
+	test_bfs(&negacycle_graphr.gr, 1, 1, 2, 3);
+	test_bfs(&negacycle_graphr.gr, 2, 2, 3, 1);
+	test_bfs(&negacycle_graphr.gr, 3, 3, 1, 2);
+
 	return exit_status();
 }

+ 5 - 1
ccan/agar/test/api-dfs.c

@@ -45,7 +45,7 @@ int main(void)
 	struct agar_state *sr;
 	const void *nr;
 	
-	plan_tests(2 * 13 + 12 + 10);
+	plan_tests(2 * 13 + 12 + 10 + 6);
 
 	test_dfs(&trivial_graphr.gr, 1, 1);
 
@@ -97,5 +97,9 @@ int main(void)
 	test_dfs_partial(sr, 1, 1, 2, 3);
 	tal_free(sr);
 
+	test_dfs(&negacycle_graphr.gr, 1, 1, 2, 3);
+	test_dfs(&negacycle_graphr.gr, 2, 2, 3, 1);
+	test_dfs(&negacycle_graphr.gr, 3, 3, 1, 2);
+
 	return exit_status();
 }

+ 12 - 1
ccan/agar/test/api-dijkstra.c

@@ -236,12 +236,22 @@ static void test_shortcut2(void)
 	tal_free(sr);
 }
 
+static void test_negacycle(void)
+{
+	struct agar_state *sr;
+
+	ok1(sr = agar_dijkstra_new(NULL, &negacycle_graphr.gr, int2ptr(1)));
+	agar_dijkstra_complete(sr);
+	ok1(agar_error(sr) == AGA_ERR_NEGATIVE_COST);
+	tal_free(sr);
+}
+
 int main(void)
 {
 	plan_tests(6 + 23
 		   + FULL_LEN * (FULL_LEN*4 - 1)
 		   + CHAIN_LEN * (1 + CHAIN_LEN*2)
-		   + 12 + 32 + 7 + 2);
+		   + 12 + 32 + 7 + 2 + 2);
 
 	test_trivial();
 	test_parallel();
@@ -251,6 +261,7 @@ int main(void)
 	test_traversal1();
 	test_shortcut1();
 	test_shortcut2();
+	test_negacycle();
 	
 	return exit_status();
 }

+ 42 - 0
ccan/agar/test/negacycle.c

@@ -0,0 +1,42 @@
+#include "config.h"
+
+#include <assert.h>
+
+#include <ccan/container_of/container_of.h>
+#include <ccan/ptrint/ptrint.h>
+
+#include <ccan/agar/agar.h>
+
+#include "simple-graphr.h"
+
+static const void *negacycle_first_edge_r(const struct agar_graph *gr,
+					  const void *nr)
+{
+	return int2ptr(1);
+}
+
+static const void *negacycle_next_edge_r(const struct agar_graph *gr,
+					 const void *nr, const void *e)
+{
+	assert(ptr2int(e) == 1);
+	return NULL;
+}
+
+static int negacycle_edge_info_r(const struct agar_graph *gr,
+				 const void *nr, const void *e,
+				 struct agar_edge_info *eir)
+{
+	int ni = ptr2int(nr);
+
+	assert(ptr2int(e) == 1);
+	eir->to = int2ptr((ni % 3) + 1);
+	if (ni == 3)
+		eir->icost = -3;
+	return 0;
+}
+
+struct negacycle_graphr negacycle_graphr = {
+	AGAR_INIT_GRAPH(negacycle_first_edge_r,
+			negacycle_next_edge_r,
+			negacycle_edge_info_r),
+};

+ 19 - 0
ccan/agar/test/simple-graphr.h

@@ -240,4 +240,23 @@ static const struct adjacency_listr shortcut2_adjacencyr[] = {
 	{},
 };
 
+/* Negacycle graph
+ *
+ *  A <---- (-3) ----- C
+ *   \                 ^
+ *    (1)->  B -- (1)-/
+ *
+ * Graph with a negative length cycle, and so lacking well-defined shortest paths.
+ */
+struct negacycle_graphr {
+	struct agar_graph gr;
+};
+extern struct negacycle_graphr negacycle_graphr;
+static const struct adjacency_listr negacycle_adjacencyr[] = {
+	{1, {2}},
+	{2, {3}},
+	{3, {1}},
+	{},
+};
+
 #endif /* _SIMPLE_GRAPHR_H */