def test_project_clone(self): g_str = [ "0 1", "3 4", "0 1 0 0 2", "0 2 1 1 1", "1 2 0 0 2", "2 3 0 0 1", "2 3 1 1 1", "1 4 0 0 2", "2 4 1 1 3", "3 4 0 0 2", ] graph = create_graph_from_text(g_str) # Test clone cloned = gtn.clone(graph) self.assertTrue(gtn.equal(graph, cloned)) # Test projecting input g_str = [ "0 1", "3 4", "0 1 0 0 2", "0 2 1 1 1", "1 2 0 0 2", "2 3 0 0 1", "2 3 1 1 1", "1 4 0 0 2", "2 4 1 1 3", "3 4 0 0 2", ] inputExpected = create_graph_from_text(g_str) self.assertTrue(gtn.equal(gtn.project_input(graph), inputExpected)) # Test projecting output g_str = [ "0 1", "3 4", "0 1 0 0 2", "0 2 1 1 1", "1 2 0 0 2", "2 3 0 0 1", "2 3 1 1 1", "1 4 0 0 2", "2 4 1 1 3", "3 4 0 0 2", ] outputExpected = create_graph_from_text(g_str) self.assertTrue(gtn.equal(gtn.project_output(graph), outputExpected))
def test_viterbi_path_grad(self): g_str = [ "0 1", "3 4", "0 1 0 0 2", "0 2 1 1 1", "1 2 0 0 2", "2 3 0 0 1", "2 3 1 1 3", "1 4 0 0 2", "2 4 1 1 3", "3 4 0 0 2", ] g = create_graph_from_text(g_str) gtn.backward(gtn.viterbi_path(g)) expected = [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0] self.assertEqual(g.grad().weights_to_list(), expected) g.zero_grad() def forward_fn(g): paths = [ gtn.viterbi_path(g), gtn.viterbi_path(g), gtn.viterbi_path(g) ] return gtn.forward_score(gtn.union(paths)) gtn.backward(forward_fn(g)) self.assertTrue(numerical_grad_check(forward_fn, g, 1e-3, 1e-5))
def test_viterbi_score_grad(self): g = gtn.Graph() g.add_node(True) g.add_node() g.add_node(False, True) g.add_arc(0, 1, 0, 0, 1) g.add_arc(0, 1, 1, 1, 2) g.add_arc(0, 1, 2, 2, 3) g.add_arc(1, 2, 0, 0, 1) g.add_arc(1, 2, 1, 1, 2) g.add_arc(1, 2, 2, 2, 3) gtn.backward(gtn.viterbi_score(g)) expected = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0] self.assertEqual(g.grad().weights_to_list(), expected) # Handle two start nodes g = gtn.Graph() g.add_node(True) g.add_node(True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, -5) g.add_arc(0, 2, 0, 0, 1) g.add_arc(1, 2, 0, 0, 2) gtn.backward(gtn.viterbi_score(g)) expected = [0.0, 0.0, 1.0] self.assertEqual(g.grad().weights_to_list(), expected) # Handle two accept nodes g = gtn.Graph() g.add_node(True) g.add_node(False, True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, 2) g.add_arc(0, 2, 0, 0, 2) g.add_arc(1, 2, 0, 0, 2) gtn.backward(gtn.viterbi_score(g)) expected = [1.0, 0.0, 1.0] self.assertEqual(g.grad().weights_to_list(), expected) # A more complex test case g_str = [ "0 1", "3 4", "0 1 0 0 2", "0 2 1 1 1", "1 2 0 0 2", "2 3 0 0 1", "2 3 1 1 1", "1 4 0 0 2", "2 4 1 1 3", "3 4 0 0 2", ] g = create_graph_from_text(g_str) gtn.backward(gtn.viterbi_score(g)) # two possible paths with same viterbi score expected1 = [1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0] expected2 = [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0] self.assertTrue(g.grad().weights_to_list() == expected1 or g.grad().weights_to_list() == expected2)
def test_viterbi_score(self): # Check score of empty graph g = gtn.Graph() self.assertEqual(gtn.viterbi_score(g).item(), -math.inf) # A simple test case g = gtn.Graph() g.add_node(True) g.add_node() g.add_node(False, True) g.add_arc(0, 1, 0, 0, 1) g.add_arc(0, 1, 1, 1, 2) g.add_arc(0, 1, 2, 2, 3) g.add_arc(1, 2, 0, 0, 1) g.add_arc(1, 2, 1, 1, 2) g.add_arc(1, 2, 2, 2, 3) self.assertEqual(gtn.viterbi_score(g).item(), 6.0) # Handle two start nodes g = gtn.Graph() g.add_node(True) g.add_node(True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, -5) g.add_arc(0, 2, 0, 0, 1) g.add_arc(1, 2, 0, 0, 2) self.assertEqual(gtn.viterbi_score(g).item(), 2.0) # Handle two accept nodes g = gtn.Graph() g.add_node(True) g.add_node(False, True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, 2) g.add_arc(0, 2, 0, 0, 2) g.add_arc(1, 2, 0, 0, 2) self.assertEqual(gtn.viterbi_score(g).item(), 4.0) # A more complex test case g_str = [ "0 1", "3 4", "0 1 0 0 2", "0 2 1 1 1", "1 2 0 0 2", "2 3 0 0 1", "2 3 1 1 1", "1 4 0 0 2", "2 4 1 1 3", "3 4 0 0 2", ] g = create_graph_from_text(g_str) self.assertEqual(gtn.viterbi_score(g).item(), 7.0)
def test_savetxt(self): # Acceptor test g = gtn.Graph() g.add_node(True) g.add_node(True) g.add_node() g.add_node() g.add_node(False, True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, 1.1) g.add_arc(1, 2, 1, 1, 2.1) g.add_arc(2, 3, 2, 2, 3.1) g.add_arc(3, 4, 3, 3, 4.1) g.add_arc(4, 5, 4, 4, 5.1) g_str = [ "0 1", "4 5", "0 1 0 0 1.1", "1 2 1 1 2.1", "2 3 2 2 3.1", "3 4 3 3 4.1", "4 5 4 4 5.1", ] g = create_graph_from_text(g_str) _, tmpfile = tempfile.mkstemp() gtn.savetxt(tmpfile, g) with open(tmpfile) as f: lines = f.readlines() self.assertEqual(len(g_str), len(lines)) for i, line in enumerate(lines): self.assertEqual(line.strip(), g_str[i]) # Transducer test g = gtn.Graph() g.add_node(True) g.add_node(True) g.add_node() g.add_node() g.add_node(False, True) g.add_node(False, True) g.add_arc(0, 1, 0, 1, 1.1) g.add_arc(1, 2, 1, 2, 2.1) g.add_arc(2, 3, 2, 3, 3.1) g.add_arc(3, 4, 3, 4, 4.1) g.add_arc(4, 5, 4, gtn.epsilon, 5.1) g_str = [ "0 1", "4 5", "0 1 0 1 1.1", "1 2 1 2 2.1", "2 3 2 3 3.1", "3 4 3 4 4.1", "4 5 4 -1 5.1", ] g = create_graph_from_text(g_str) _, tmpfile = tempfile.mkstemp() gtn.savetxt(tmpfile, g) with open(tmpfile) as f: lines = f.readlines() self.assertEqual(len(g_str), len(lines)) for i, line in enumerate(lines): self.assertEqual(line.strip(), g_str[i])
def test_loadtxt(self): g1 = gtn.Graph() g1.add_node(True, True) g1.add_node(False, True) g1.add_node() g1.add_arc(0, 0, 1) g1.add_arc(0, 2, 1, 1, 1.1) g1.add_arc(2, 1, 2, 2, 2.1) g_str = ["0", "0 1", "0 0 1 1 0", "0 2 1 1 1.1", "2 1 2 2 2.1"] g2 = create_graph_from_text(g_str) self.assertTrue(gtn.equal(g1, g2)) self.assertTrue(gtn.isomorphic(g1, g2)) _, tmpfile = tempfile.mkstemp() # Write the test file gtn.savetxt(tmpfile, g2) g3 = gtn.loadtxt(tmpfile) self.assertTrue(gtn.equal(g1, g3)) # Empty graph doesn't load g_str = [""] self.assertRaises(ValueError, create_graph_from_text, g_str) # Graph without accept nodes doesn't load g_str = ["1"] self.assertRaises(ValueError, create_graph_from_text, g_str) # Graph with repeat start nodes doesn't load g_str = ["1 0 0", "0 1"] self.assertRaises(ValueError, create_graph_from_text, g_str) # Graph loads if the start and accept nodes are specified g_str = ["0", "1"] g = gtn.Graph() g.add_node(True) g.add_node(False, True) self.assertTrue(gtn.equal(g, create_graph_from_text(g_str))) # Graph doesn't load if arc incorrect g_str = ["0", "1", "0 2"] self.assertRaises(ValueError, create_graph_from_text, g_str) g_str = ["0", "1", "0 1 2 3 4 5"] self.assertRaises(ValueError, create_graph_from_text, g_str) # Transducer loads g1 = gtn.Graph() g1.add_node(True, True) g1.add_node(False, True) g1.add_node() g1.add_arc(0, 0, 1, 1) g1.add_arc(0, 2, 1, 2, 1.1) g1.add_arc(2, 1, 2, 3, 2.1) g_str = ["0", "0 1", "0 0 1", "0 2 1 2 1.1", "2 1 2 3 2.1"] g2 = create_graph_from_text(g_str) self.assertTrue(gtn.equal(g1, g2)) self.assertTrue(gtn.isomorphic(g1, g2))
def test_forward_score_grad(self): g = gtn.Graph() g.add_node(True) g.add_node() g.add_node(False, True) g.add_arc(0, 1, 0, 0, 1) g.add_arc(0, 1, 1, 1, 2) g.add_arc(0, 1, 2, 2, 3) g.add_arc(1, 2, 0, 0, 1) g.add_arc(1, 2, 1, 1, 2) g.add_arc(1, 2, 2, 2, 3) gtn.backward(gtn.forward_score(g)) self.assertTrue(numerical_grad_check(gtn.forward_score, g, 1e-3, 1e-3)) # Handle two start nodes g = gtn.Graph() g.add_node(True) g.add_node(True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, -5) g.add_arc(0, 2, 0, 0, 1) g.add_arc(1, 2, 0, 0, 2) gtn.backward(gtn.forward_score(g)) self.assertTrue(numerical_grad_check(gtn.forward_score, g, 1e-3, 1e-3)) denom = 1 / (math.exp(-3) + math.exp(1) + math.exp(2)) grad = g.grad() grad_weights = grad.weights_to_list() self.assertAlmostEqual(grad_weights[0], (denom * math.exp(-3))) self.assertAlmostEqual(grad_weights[1], (denom * math.exp(1))) self.assertAlmostEqual(grad_weights[2], (denom * (math.exp(-3) + math.exp(2)))) # Handle two accept nodes g = gtn.Graph() g.add_node(True) g.add_node(False, True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, 2) g.add_arc(0, 2, 0, 0, 2) g.add_arc(1, 2, 0, 0, 2) gtn.backward(gtn.forward_score(g)) self.assertTrue(numerical_grad_check(gtn.forward_score, g, 1e-3, 1e-3)) denom = 1 / (2 * math.exp(2) + math.exp(4)) grad = g.grad() grad_weights = grad.weights_to_list() self.assertAlmostEqual(grad_weights[0], (denom * (math.exp(2) + math.exp(4))), places=5) self.assertAlmostEqual(grad_weights[1], (denom * math.exp(2)), places=5) self.assertAlmostEqual(grad_weights[2], (denom * math.exp(4)), places=5) # Handle case where some arcs don't lead to accepting states g = gtn.Graph() g.add_node(True) g.add_node(False, False) g.add_node(False, True) g.add_arc(0, 1, 0, 0, 2) g.add_arc(0, 2, 0, 0, 2) gtn.backward(gtn.forward_score(g)) self.assertTrue(numerical_grad_check(gtn.forward_score, g, 1e-3, 1e-3)) grad = g.grad() grad_weights = grad.weights_to_list() self.assertAlmostEqual(grad_weights[0], (0.0)) self.assertAlmostEqual(grad_weights[1], (1.0)) # Handles negative infinity g = gtn.Graph() g.add_node(True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, -math.inf) g.add_arc(0, 1, 1, 1, -math.inf) gtn.backward(gtn.forward_score(g)) grad = g.grad() grad_weights = grad.weights_to_list() self.assertTrue(math.isnan(grad_weights[0])) self.assertTrue(math.isnan(grad_weights[1])) g2 = gtn.Graph() g2.add_node(True) g2.add_node(False, True) g2.add_arc(0, 1, 0, 0, -math.inf) g2.add_arc(0, 1, 1, 1, 1.0) gtn.backward(gtn.forward_score(g2)) grad2 = g2.grad() grad_weights = grad2.weights_to_list() self.assertAlmostEqual(grad_weights[0], (0.0)) self.assertAlmostEqual(grad_weights[1], (1.0)) # Handles infinity g = gtn.Graph() g.add_node(True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, math.inf) g.add_arc(0, 1, 1, 1, math.inf) gtn.backward(gtn.forward_score(g)) grad = g.grad() grad_weights = grad.weights_to_list() self.assertTrue(math.isnan(grad_weights[0])) self.assertTrue(math.isnan(grad_weights[1])) g2 = gtn.Graph() g2.add_node(True) g2.add_node(False, True) g2.add_arc(0, 1, 0, 0, math.inf) g2.add_arc(0, 1, 1, 1, 1.0) gtn.backward(gtn.forward_score(g2)) grad2 = g2.grad() grad_weights = grad.weights_to_list() self.assertTrue(math.isnan(grad_weights[0])) self.assertTrue(math.isnan(grad_weights[1])) # A more complex test case g_str = [ "0 1", "3 4", "0 1 0 0 2", "0 2 1 1 1", "1 2 0 0 2", "2 3 0 0 1", "2 3 1 1 1", "1 4 0 0 2", "2 4 1 1 3", "3 4 0 0 2", ] g = create_graph_from_text(g_str) gtn.backward(gtn.forward_score(g)) self.assertTrue(numerical_grad_check(gtn.forward_score, g, 1e-3, 1e-3))
def test_composition(self): # Compos,ing with an empty graph gives an empty graph g1 = gtn.Graph() g2 = gtn.Graph() self.assertTrue(gtn.equal(gtn.compose(g1, g2), gtn.Graph())) g1.add_node(True) g1.add_node() g1.add_arc(0, 1, 0) g2.add_node(True) g2.add_node(False, True) g2.add_arc(0, 1, 0) g2.add_arc(0, 1, 0) self.assertTrue(gtn.equal(gtn.compose(g1, g2), gtn.Graph())) self.assertTrue(gtn.equal(gtn.compose(g2, g1), gtn.Graph())) self.assertTrue(gtn.equal(gtn.intersect(g2, g1), gtn.Graph())) # Check singly sorted version g1.arc_sort(True) self.assertTrue(gtn.equal(gtn.compose(g1, g2), gtn.Graph())) # Check doubly sorted version g2.arc_sort() self.assertTrue(gtn.equal(gtn.compose(g1, g2), gtn.Graph())) # Self-loop in the composed graph g1 = gtn.Graph() g1.add_node(True) g1.add_node(False, True) g1.add_arc(0, 0, 0) g1.add_arc(0, 1, 1) g1.add_arc(1, 1, 2) g2 = gtn.Graph() g2.add_node(True) g2.add_node() g2.add_node(False, True) g2.add_arc(0, 1, 0) g2.add_arc(1, 1, 0) g2.add_arc(1, 2, 1) g_str = ["0", "2", "0 1 0", "1 1 0", "1 2 1"] expected = create_graph_from_text(g_str) self.assertTrue(gtn.isomorphic(gtn.compose(g1, g2), expected)) self.assertTrue(gtn.isomorphic(gtn.intersect(g1, g2), expected)) # Check singly sorted version g1.arc_sort(True) self.assertTrue(gtn.isomorphic(gtn.compose(g1, g2), expected)) # Check doubly sorted version g2.arc_sort() self.assertTrue(gtn.isomorphic(gtn.compose(g1, g2), expected)) # Loop in the composed graph g1 = gtn.Graph() g1.add_node(True) g1.add_node(False, True) g1.add_arc(0, 1, 0) g1.add_arc(1, 1, 1) g1.add_arc(1, 0, 0) g2 = gtn.Graph() g2.add_node(True) g2.add_node(False, True) g2.add_arc(0, 0, 0) g2.add_arc(0, 1, 1) g2.add_arc(1, 0, 1) g_str = ["0", "2", "0 1 0", "1 0 0", "1 2 1", "2 1 1"] expected = create_graph_from_text(g_str) self.assertTrue(gtn.isomorphic(gtn.compose(g1, g2), expected)) self.assertTrue(gtn.isomorphic(gtn.intersect(g1, g2), expected)) # Check singly sorted version g1.arc_sort(True) self.assertTrue(gtn.isomorphic(gtn.compose(g1, g2), expected)) # Check doubly sorted version g2.arc_sort() self.assertTrue(gtn.isomorphic(gtn.compose(g1, g2), expected)) g1 = gtn.Graph() g1.add_node(True) g1.add_node() g1.add_node() g1.add_node() g1.add_node(False, True) for i in range(g1.num_nodes() - 1): for j in range(3): g1.add_arc(i, i + 1, j, j, j) g2 = gtn.Graph() g2.add_node(True) g2.add_node() g2.add_node(False, True) g2.add_arc(0, 1, 0, 0, 3.5) g2.add_arc(1, 1, 0, 0, 2.5) g2.add_arc(1, 2, 1, 1, 1.5) g2.add_arc(2, 2, 1, 1, 4.5) g_str = [ "0", "6", "0 1 0 0 3.5", "1 2 0 0 2.5", "1 4 1 1 2.5", "2 3 0 0 2.5", "2 5 1 1 2.5", "4 5 1 1 5.5", "3 6 1 1 2.5", "5 6 1 1 5.5", ] expected = create_graph_from_text(g_str) self.assertTrue(gtn.isomorphic(gtn.compose(g1, g2), expected)) self.assertTrue(gtn.isomorphic(gtn.intersect(g1, g2), expected)) # Check singly sorted version g1.arc_sort(True) self.assertTrue(gtn.isomorphic(gtn.compose(g1, g2), expected)) # Check doubly sorted version g2.arc_sort() self.assertTrue(gtn.isomorphic(gtn.compose(g1, g2), expected))
def test_viterbi_path(self): g = gtn.Graph() # Empty graph gives empty path self.assertTrue(gtn.equal(gtn.viterbi_path(g), g)) # Accepting empty string g.add_node(True, True) self.assertTrue(gtn.equal(gtn.viterbi_path(g), g)) # A simple test case g = gtn.Graph() g.add_node(True) g.add_node() g.add_node(False, True) g.add_arc(0, 1, 0, 0, 1) g.add_arc(0, 1, 1, 1, 2) g.add_arc(0, 1, 2, 2, 3) g.add_arc(1, 2, 0, 0, 1) g.add_arc(1, 2, 1, 1, 2) g.add_arc(1, 2, 2, 2, 3) best = gtn.Graph() best.add_node(True) best.add_node() best.add_node(False, True) best.add_arc(0, 1, 2, 2, 3) best.add_arc(1, 2, 2, 2, 3) path = gtn.viterbi_path(g) self.assertTrue(gtn.rand_equivalent(path, best)) self.assertEqual(gtn.viterbi_score(path).item(), gtn.viterbi_score(g).item()) # Handle a single node. g = gtn.Graph() g.add_node(True, True) best = gtn.Graph() best.add_node(True, True) path = gtn.viterbi_path(g) self.assertTrue(gtn.rand_equivalent(path, best)) self.assertEqual(gtn.viterbi_score(path).item(), gtn.viterbi_score(g).item()) # Handle two start nodes g = gtn.Graph() g.add_node(True) g.add_node(True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, -5) g.add_arc(0, 2, 0, 0, 1) g.add_arc(1, 2, 0, 0, 2) best = gtn.Graph() best.add_node(True) best.add_node(False, True) best.add_arc(0, 1, 0, 0, 2) path = gtn.viterbi_path(g) self.assertTrue(gtn.rand_equivalent(path, best)) self.assertEqual(gtn.viterbi_score(path).item(), gtn.viterbi_score(g).item()) # Handle two accept nodes g = gtn.Graph() g.add_node(True) g.add_node(False, True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, 3) g.add_arc(0, 2, 0, 0, 2) g.add_arc(1, 2, 0, 0, 2) best = gtn.Graph() best.add_node(True) best.add_node() best.add_node(False, True) best.add_arc(0, 1, 0, 0, 3) best.add_arc(1, 2, 0, 0, 2) path = gtn.viterbi_path(g) self.assertTrue(gtn.rand_equivalent(path, best)) self.assertEqual(gtn.viterbi_score(path).item(), gtn.viterbi_score(g).item()) # A more complex test case g_str = [ "0 1", "3 4", "0 1 0 0 2", "0 2 1 1 1", "1 2 0 0 2", "2 3 0 0 1", "2 3 1 1 1", "1 4 0 0 2", "2 4 1 1 3", "3 4 0 0 2", ] g = create_graph_from_text(g_str) # There are three options for the best path, the # viterbiPath may return any of them. best1 = gtn.Graph() best1.add_node(True) best1.add_node() best1.add_node() best1.add_node() best1.add_node(False, True) best1.add_arc(0, 1, 0, 0, 2) best1.add_arc(1, 2, 0, 0, 2) best1.add_arc(2, 3, 0, 0, 1) best1.add_arc(3, 4, 0, 0, 2) best2 = gtn.Graph() best2.add_node(True) best2.add_node() best2.add_node() best2.add_node() best2.add_node(False, True) best2.add_arc(0, 1, 0, 0, 2) best2.add_arc(1, 2, 0, 0, 2) best2.add_arc(2, 3, 1, 1, 1) best2.add_arc(3, 4, 0, 0, 2) best3 = gtn.Graph() best3.add_node(True) best3.add_node() best3.add_node() best3.add_node(False, True) best3.add_arc(0, 1, 0, 0, 2) best3.add_arc(1, 2, 0, 0, 2) best3.add_arc(2, 3, 1, 1, 3) path = gtn.viterbi_path(g) self.assertTrue( ( gtn.rand_equivalent(path, best1) or gtn.rand_equivalent(path, best2) or gtn.rand_equivalent(path, best3) ) ) self.assertEqual(gtn.viterbi_score(path).item(), gtn.viterbi_score(g).item())
def test_forward(self): # Check score of empty graph g = gtn.Graph() self.assertEqual(gtn.forward_score(g).item(), -math.inf) # Throws on self-loops g = gtn.Graph() g.add_node(True, True) g.add_arc(0, 0, 1) self.assertRaises(ValueError, gtn.forward_score, g) # Throws on internal self-loop g = gtn.Graph() g.add_node(True) g.add_node() g.add_node(False, True) g.add_arc(0, 1, 0) g.add_arc(1, 2, 0) g.add_arc(1, 1, 0) self.assertRaises(ValueError, gtn.forward_score, g) # Throws on self-loop in accept node g = gtn.Graph() g.add_node(True) g.add_node() g.add_node(False, True) g.add_arc(0, 1, 0) g.add_arc(1, 2, 0) g.add_arc(2, 2, 0) self.assertRaises(ValueError, gtn.forward_score, g) # Throws on cycle g = gtn.Graph() g.add_node(True) g.add_node() g.add_node(False, True) g.add_arc(0, 1, 0) g.add_arc(1, 2, 0) g.add_arc(2, 0, 0) self.assertRaises(ValueError, gtn.forward_score, g) # Throws if a non-start node has no incoming arcs g = gtn.Graph() g.add_node(True) g.add_node() g.add_node(False, True) g.add_arc(0, 2, 0) g.add_arc(1, 2, 0) self.assertRaises(ValueError, gtn.forward_score, g) # Handles negative infinity g = gtn.Graph() g.add_node(True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, -math.inf) g.add_arc(0, 1, 1, 1, -math.inf) self.assertEqual(gtn.forward_score(g).item(), -math.inf) # Handles infinity g = gtn.Graph() g.add_node(True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, math.inf) g.add_arc(0, 1, 1, 1, 0) self.assertEqual(gtn.forward_score(g).item(), math.inf) # Single Node g = gtn.Graph() g.add_node(True, True) self.assertEqual(gtn.forward_score(g).item(), 0.0) # A simple test case g = gtn.Graph() g.add_node(True) g.add_node() g.add_node(False, True) g.add_arc(0, 1, 0, 0, 1) g.add_arc(0, 1, 1, 1, 2) g.add_arc(0, 1, 2, 2, 3) g.add_arc(1, 2, 0, 0, 1) g.add_arc(1, 2, 1, 1, 2) g.add_arc(1, 2, 2, 2, 3) self.assertAlmostEqual(gtn.forward_score(g).item(), (6.8152), places=4) # Handle two start nodes g = gtn.Graph() g.add_node(True) g.add_node(True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, -5) g.add_arc(0, 2, 0, 0, 1) g.add_arc(1, 2, 0, 0, 2) expected = math.log(math.exp(1) + math.exp(-5 + 2) + math.exp(2)) self.assertAlmostEqual(gtn.forward_score(g).item(), (expected), places=4) # Handle two accept nodes g = gtn.Graph() g.add_node(True) g.add_node(False, True) g.add_node(False, True) g.add_arc(0, 1, 0, 0, 2) g.add_arc(0, 2, 0, 0, 2) g.add_arc(1, 2, 0, 0, 2) expected = math.log(2 * math.exp(2) + math.exp(4)) self.assertAlmostEqual(gtn.forward_score(g).item(), (expected), places=4) # Handle case where some arcs don't lead to accepting states g = gtn.Graph() g.add_node(True) g.add_node(False, False) g.add_node(False, True) g.add_arc(0, 1, 0, 0, 2) g.add_arc(0, 2, 0, 0, 2) self.assertEqual(gtn.forward_score(g).item(), 2.0) # A more complex test case g_str = [ "0 1", "3 4", "0 1 0 0 2", "0 2 1 1 1", "1 2 0 0 2", "2 3 0 0 1", "2 3 1 1 1", "1 4 0 0 2", "2 4 1 1 3", "3 4 0 0 2", ] g = create_graph_from_text(g_str) self.assertAlmostEqual(gtn.forward_score(g).item(), (8.36931), places=4)