def test_loadsave(self): _, tmpfile = tempfile.mkstemp() g = gtn.Graph() gtn.save(tmpfile, g) g2 = gtn.load(tmpfile) self.assertTrue(gtn.equal(g, g2)) 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) gtn.save(tmpfile, g) g2 = gtn.load(tmpfile) self.assertTrue(gtn.equal(g, g2)) self.assertTrue(gtn.isomorphic(g, g2))
def test_sum(self): # Empty graph self.assertTrue(gtn.equal(gtn.union([]), gtn.Graph())) # Check single graph is a no-op g1 = gtn.Graph() g1.add_node(True) g1.add_node(False, True) g1.add_arc(0, 1, 1) self.assertTrue(gtn.equal(gtn.union([g1]), g1)) # Simple union g1 = gtn.Graph() g1.add_node(True) g1.add_node(False, True) g1.add_arc(0, 1, 1) g2 = gtn.Graph() g2.add_node(True) g2.add_node(False, True) g2.add_arc(0, 1, 0) expected = gtn.Graph() expected.add_node(True) expected.add_node(True) expected.add_node(False, True) expected.add_node(False, True) expected.add_arc(0, 2, 1) expected.add_arc(1, 3, 0) self.assertTrue(gtn.isomorphic(gtn.union([g1, g2]), expected)) # Check adding with an empty graph works g1 = gtn.Graph() g1.add_node(True) g1.add_node(False, True) g1.add_arc(0, 1, 1) g2 = gtn.Graph() g3 = gtn.Graph() g3.add_node(True, True) g3.add_arc(0, 0, 2) expected = gtn.Graph() expected.add_node(True) expected.add_node(False, True) expected.add_node(True, True) expected.add_arc(0, 1, 1) expected.add_arc(2, 2, 2) self.assertTrue(gtn.isomorphic(gtn.union([g1, g2, g3]), expected))
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_backward_calls_once(self): g1 = gtn.scalar_graph(1) g2 = gtn.scalar_graph(1) gout = gtn.add(g1, g2) gtn.backward([gout]) pmap_grad = gout.grad() gout = gtn.add(g1, g2) gtn.backward(gout) grad = gout.grad() self.assertTrue(gtn.equal(pmap_grad, grad))
def test_parallel_one_arg(self): inputs = [gtn.scalar_graph(k) for k in [1.0, 2.0, 3.0]] outputs = gtn.negate(inputs) expected = [] for g in inputs: expected.append(gtn.negate(g)) self.assertEqual(len(outputs), len(inputs)) for i in range(0, len(expected)): self.assertTrue(gtn.equal(outputs[i], expected[i]))
def test_default_args(self): g1 = gtn.scalar_graph(0) g1.add_arc(0, 1, 0) g2 = gtn.scalar_graph(0) g2.add_arc(0, 1, 0) g_expected = gtn.remove(g1) g_removed = gtn.remove([g1, g2])[0] self.assertTrue(gtn.equal(g_expected, g_removed))
def test_comparisons(self): g1 = gtn.Graph() g1.add_node(True) g1.add_node(False, True) g1.add_arc(0, 1, 0) g2 = gtn.Graph() g2.add_node(True) g2.add_node(False, True) g2.add_arc(0, 1, 0) self.assertTrue(gtn.equal(g1, g2)) self.assertTrue(gtn.isomorphic(g1, g2)) g2 = gtn.Graph() g2.add_node(False, True) g2.add_node(True) g2.add_arc(1, 0, 0) self.assertFalse(gtn.equal(g1, g2)) self.assertTrue(gtn.isomorphic(g1, g2))
def test_parallel_two_arg(self): inputs1 = [gtn.scalar_graph(k) for k in [1.0, 2.0, 3.0]] inputs2 = [gtn.scalar_graph(k) for k in [1.0, 2.0, 3.0]] outputs = gtn.add(inputs1, inputs2) expected = [] for g1, g2 in zip(inputs1, inputs2): expected.append(gtn.add(g1, g2)) self.assertEqual(len(outputs), len(inputs1), len(inputs2)) for i in range(0, len(expected)): self.assertTrue(gtn.equal(outputs[i], expected[i]))
def test_kernel_graph(self): def get_graph(l1, l2, add_skip=False): g = gtn.Graph() g.add_node(True) g.add_node(True) g.add_node() g.add_node(False, True) g.add_node(False, True) g.add_arc(0, 0, 2) g.add_arc(0, 1, l1) g.add_arc(1, 1, l1) g.add_arc(1, 2, 2) g.add_arc(2, 2, 2) g.add_arc(2, 3, l2) g.add_arc(3, 3, l2) g.add_arc(3, 4, 2) g.add_arc(4, 4, 2) if add_skip: g.add_arc(1, 3, l2) return g # Repeats with optional blank graph = transducer.make_kernel_graph([0, 0], 2, True) gtn.equal(graph, get_graph(0, 0, False)) # No repeats without optional blank graph = transducer.make_kernel_graph([0, 1], 2, False) gtn.equal(graph, get_graph(0, 1, False)) # No repeats with optional blank graph = transducer.make_kernel_graph([0, 1], 2, True) gtn.equal(graph, get_graph(0, 1, True))
def test_parallel_backward(self): inputs1 = [gtn.scalar_graph(k) for k in [1.0, 2.0, 3.0]] inputs2 = [gtn.scalar_graph(k) for k in [1.0, 2.0, 3.0]] outputs = gtn.add(inputs1, inputs2) gtn.backward(outputs) # Test gradients inputs1 = [gtn.scalar_graph(k) for k in [1.0, 2.0, 3.0]] inputs2 = [gtn.scalar_graph(k) for k in [1.0, 2.0, 3.0]] outputs = gtn.add(inputs1, inputs2) gradIn = gtn.scalar_graph(5.0) gtn.backward(outputs, [gradIn], [False]) inputs1Dup = [gtn.scalar_graph(k) for k in [1.0, 2.0, 3.0]] inputs2Dup = [gtn.scalar_graph(k) for k in [1.0, 2.0, 3.0]] expected = [] for g1, g2 in zip(inputs1Dup, inputs2Dup): expected.append(gtn.add(g1, g2)) for g in expected: gtn.backward(g, gtn.scalar_graph(5.0)) for i in range(0, len(expected)): self.assertTrue(gtn.equal(inputs1[i].grad(), inputs1Dup[i].grad())) self.assertTrue(gtn.equal(inputs2[i].grad(), inputs2Dup[i].grad()))
def test_parallel_vector_arg(self): inputList = [ gtn.scalar_graph(1.0), gtn.scalar_graph(2.0), gtn.scalar_graph(3.0), ] inputs = [inputList, inputList, inputList] outputs = gtn.concat(inputs) expected = [] for gList in inputs: expected.append(gtn.concat(gList)) self.assertEqual(len(outputs), len(inputs)) for i in range(0, len(expected)): self.assertTrue(gtn.equal(outputs[i], expected[i]))
def test_clone_project_grad(self): g1 = gtn.scalar_graph(3.0) g2 = gtn.scalar_graph(4.0) cloned = gtn.clone(g1) result = gtn.add(g1, g2) gtn.backward(result) # Cloned wasn't used in the computation self.assertRaises(RuntimeError, cloned.grad) # Cloned was used in the computation g1.zero_grad() g2.zero_grad() result = gtn.add(cloned, g2) gtn.backward(result) self.assertTrue(gtn.equal(cloned.grad(), g1.grad()))
def test_parallel_func(self): B = 3 inputs1 = [gtn.scalar_graph(k) for k in [1.0, 2.0, 3.0]] inputs2 = [gtn.scalar_graph(k) for k in [1.0, 2.0, 3.0]] out = [None] * B def process(b): out[b] = gtn.add(gtn.add(inputs1[b], inputs1[b]), gtn.negate(inputs2[b])) gtn.parallel_for(process, range(B)) expected = [] for b in range(B): expected.append( gtn.add(gtn.add(inputs1[b], inputs1[b]), gtn.negate(inputs2[b]))) self.assertEqual(len(out), len(expected)) for i in range(len(expected)): self.assertTrue(gtn.equal(out[i], expected[i]))
def test_closure(self): # Empty graph expected = gtn.Graph() expected.add_node(True, True) self.assertTrue(gtn.equal(gtn.closure(gtn.Graph()), expected)) # Multi-start, multi-accept g = gtn.Graph() g.add_node(True) g.add_node(True) g.add_node(False, True) g.add_node(False, True) g.add_arc(0, 2, 0, 0, 0.0) g.add_arc(0, 3, 1, 2, 2.1) g.add_arc(1, 2, 0, 1, 1.0) g.add_arc(1, 3, 1, 3, 3.1) expected = gtn.Graph() expected.add_node(True, True) expected.add_node() expected.add_node() expected.add_node(False, True) expected.add_node(False, True) expected.add_arc(0, 1, gtn.epsilon) expected.add_arc(0, 2, gtn.epsilon) expected.add_arc(1, 3, 0, 0, 0.0) expected.add_arc(1, 4, 1, 2, 2.1) expected.add_arc(2, 3, 0, 1, 1.0) expected.add_arc(2, 4, 1, 3, 3.1) expected.add_arc(3, 1, gtn.epsilon) expected.add_arc(3, 2, gtn.epsilon) expected.add_arc(4, 1, gtn.epsilon) expected.add_arc(4, 2, gtn.epsilon) self.assertTrue(gtn.rand_equivalent(gtn.closure(g), expected))
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_epsilon_composition(self): # Simple test case for output epsilon on first graph g1 = gtn.Graph() g1.add_node(True) g1.add_node(False, True) g1.add_arc(0, 0, 0, gtn.epsilon, 1.0) g1.add_arc(0, 1, 1, 2) g2 = gtn.Graph() g2.add_node(True) g2.add_node(False, True) g2.add_arc(0, 1, 2, 3) expected = gtn.Graph() expected.add_node(True) expected.add_node(False, True) expected.add_arc(0, 0, 0, gtn.epsilon, 1.0) expected.add_arc(0, 1, 1, 3) self.assertTrue(gtn.equal(gtn.compose(g1, g2), expected)) # Simple test case for input epsilon on second graph g1 = gtn.Graph() g1.add_node(True) g1.add_node(False, True) g1.add_arc(0, 1, 1, 2) g2 = gtn.Graph() g2.add_node(True) g2.add_node(False, True) g2.add_arc(0, 1, 2, 3) g2.add_arc(1, 1, gtn.epsilon, 0, 2.0) expected = gtn.Graph() expected.add_node(True) expected.add_node(False, True) expected.add_arc(0, 1, 1, 3) expected.add_arc(1, 1, gtn.epsilon, 0, 2.0) self.assertTrue(gtn.equal(gtn.compose(g1, g2), expected)) # This test case is taken from "Weighted Automata Algorithms", Mehryar # Mohri, https://cs.nyu.edu/~mohri/pub/hwa.pdf Section 5.1, Figure 7 symbols = {"a": 0, "b": 1, "c": 2, "d": 3, "e": 4} g1 = gtn.Graph() g1.add_node(True) g1.add_node() g1.add_node() g1.add_node() g1.add_node(False, True) g1.add_arc(0, 1, symbols["a"], symbols["a"]) g1.add_arc(1, 2, symbols["b"], gtn.epsilon) g1.add_arc(2, 3, symbols["c"], gtn.epsilon) g1.add_arc(3, 4, symbols["d"], symbols["d"]) g2 = gtn.Graph() g2.add_node(True) g2.add_node() g2.add_node() g2.add_node(False, True) g2.add_arc(0, 1, symbols["a"], symbols["d"]) g2.add_arc(1, 2, gtn.epsilon, symbols["e"]) g2.add_arc(2, 3, symbols["d"], symbols["a"]) expected = gtn.Graph() expected.add_node(True) expected.add_node() expected.add_node() expected.add_node() expected.add_node(False, True) expected.add_arc(0, 1, symbols["a"], symbols["d"]) expected.add_arc(1, 2, symbols["b"], symbols["e"]) expected.add_arc(2, 3, symbols["c"], gtn.epsilon) expected.add_arc(3, 4, symbols["d"], symbols["a"]) self.assertTrue(gtn.rand_equivalent(gtn.compose(g1, g2), expected)) # Test multiple input/output epsilon transitions per node g1 = gtn.Graph() g1.add_node(True) g1.add_node(False, True) g1.add_arc(0, 0, 1, gtn.epsilon, 1.1) g1.add_arc(0, 1, 2, gtn.epsilon, 2.1) g1.add_arc(0, 1, 3, gtn.epsilon, 3.1) g2 = gtn.Graph() g2.add_node(True) g2.add_node(False, True) g2.add_arc(0, 1, gtn.epsilon, 3, 2.1) g2.add_arc(0, 1, 1, 2) expected = gtn.Graph() expected.add_node(True) expected.add_node(False, True) expected.add_arc(0, 0, 1, gtn.epsilon, 1.1) expected.add_arc(0, 1, 2, 3, 4.2) expected.add_arc(0, 1, 3, 3, 5.2) self.assertTrue(gtn.rand_equivalent(gtn.compose(g1, g2), expected))
def test_concat(self): # Empty string language g = gtn.Graph() g.add_node(True, True) self.assertTrue(gtn.equal(gtn.concat([]), g)) self.assertTrue(gtn.rand_equivalent(gtn.concat([g, g]), g)) self.assertTrue(gtn.rand_equivalent(gtn.concat([g, g, g]), g)) # Singleton g = gtn.Graph() g.add_node(True) g.add_node(False, True) g.add_arc(0, 1, 1) self.assertTrue(gtn.equal(gtn.concat([g]), g)) # Empty language g = gtn.Graph() g.add_node() self.assertTrue(gtn.rand_equivalent(gtn.concat([g, g]), gtn.Graph())) self.assertTrue(gtn.rand_equivalent(gtn.concat([g, g, g]), gtn.Graph())) # Concat 0 and 1 to get 01 g1 = gtn.Graph() g1.add_node(True) g1.add_node(False, True) g1.add_arc(0, 1, 0) g2 = gtn.Graph() g2.add_node(True) g2.add_node(False, True) g2.add_arc(0, 1, 1) expected = gtn.Graph() expected.add_node(True) expected.add_node() expected.add_node(False, True) expected.add_arc(0, 1, 0) expected.add_arc(1, 2, 1) self.assertTrue(gtn.rand_equivalent(gtn.concat([g1, g2]), expected)) # Concat 0, 1 and 2, 3 to get 02, 03, 12, 13 g1 = gtn.Graph() g1.add_node(True) g1.add_node(False, True) g1.add_node(False, True) g1.add_arc(0, 1, 0) g1.add_arc(0, 2, 1) g2 = gtn.Graph() g2.add_node(True) g2.add_node(True) g2.add_node(False, True) g2.add_arc(0, 2, 2) g2.add_arc(1, 2, 3) expected = gtn.Graph() expected.add_node(True) expected.add_node() expected.add_node(False, True) expected.add_arc(0, 1, 0) expected.add_arc(0, 1, 1) expected.add_arc(1, 2, 2) expected.add_arc(1, 2, 3) self.assertTrue(gtn.rand_equivalent(gtn.concat([g1, g2]), expected))
def test_remove(self): g = gtn.Graph(False) g.add_node(True) g.add_node() g.add_node(False, True) g.add_arc(0, 1, gtn.epsilon) g.add_arc(1, 2, 0) expected = gtn.Graph() expected.add_node(True) expected.add_node(False, True) expected.add_arc(0, 1, 0) self.assertTrue(gtn.equal(gtn.remove(g, gtn.epsilon), expected)) # Check gradient status propagates correctly self.assertFalse(gtn.remove(g, gtn.epsilon).calc_grad) # Removing other labels works g = gtn.Graph() g.add_node(True) g.add_node() g.add_node(False, True) g.add_arc(0, 1, 2, 1) g.add_arc(1, 2, 0, 1) expected = gtn.Graph() expected.add_node(True) expected.add_node(False, True) expected.add_arc(0, 1, 0, 1) self.assertTrue(gtn.equal(gtn.remove(g, 2, 1), expected)) # No-op on graph without epsilons g = gtn.Graph() g.add_node(True) g.add_node(False, True) g.add_arc(0, 1, 0, 1) g.add_arc(0, 1, 1, 1) self.assertTrue(gtn.equal(gtn.remove(g), g)) # Epsilon only transitions into accepting state g = gtn.Graph() g.add_node(True) g.add_node() g.add_node() g.add_node(False, True) g.add_arc(0, 1, 0) g.add_arc(0, 2, 1) g.add_arc(1, 3, gtn.epsilon) g.add_arc(2, 3, gtn.epsilon) expected = gtn.Graph() expected.add_node(True) expected.add_node(False, True) expected.add_node(False, True) expected.add_arc(0, 1, 0) expected.add_arc(0, 2, 1) self.assertTrue(gtn.equal(gtn.remove(g), expected)) # Only remove an arc, no removed nodes g = gtn.Graph() g.add_node(True) g.add_node() g.add_node() g.add_node(False, True) g.add_arc(0, 1, gtn.epsilon) g.add_arc(0, 2, 1) g.add_arc(2, 1, 0) g.add_arc(1, 3, 1) expected = gtn.Graph() expected.add_node(True) expected.add_node() expected.add_node() expected.add_node(False, True) expected.add_arc(0, 2, 1) expected.add_arc(2, 1, 0) expected.add_arc(1, 3, 1) expected.add_arc(0, 3, 1) self.assertTrue(gtn.equal(gtn.remove(g), expected)) # Successive epsilons g = gtn.Graph() g.add_node(True) g.add_node() g.add_node() g.add_node() g.add_node(False, True) g.add_arc(0, 1, 0) g.add_arc(1, 2, gtn.epsilon) g.add_arc(2, 3, gtn.epsilon) g.add_arc(2, 4, 1) g.add_arc(3, 4, 2) g.add_arc(1, 4, 0) expected = gtn.Graph() expected.add_node(True) expected.add_node() expected.add_node(False, True) expected.add_arc(0, 1, 0) expected.add_arc(1, 2, 0) expected.add_arc(1, 2, 1) expected.add_arc(1, 2, 2) self.assertTrue(gtn.equal(gtn.remove(g), expected)) # Multiple interior removals g = gtn.Graph() g.add_node(True) g.add_node() g.add_node() g.add_node() g.add_node(False, True) g.add_arc(0, 1, gtn.epsilon) g.add_arc(1, 2, gtn.epsilon) g.add_arc(2, 3, 0) g.add_arc(3, 4, 0) expected = gtn.Graph() expected.add_node(True) expected.add_node() expected.add_node(False, True) expected.add_arc(0, 1, 0) expected.add_arc(1, 2, 0) self.assertTrue(gtn.equal(gtn.remove(g), 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_equality(self): # Empty graph is equal to itself g1 = gtn.Graph() g2 = gtn.Graph() self.assertTrue(gtn.equal(g1, g2)) # Different start node g1 = gtn.Graph() g1.add_node(True) g2 = gtn.Graph() g2.add_node(False) self.assertFalse(gtn.equal(g1, g2)) # Simple equality g1 = gtn.Graph() g1.add_node(True) g1.add_node() g2 = gtn.Graph() g2.add_node(True) g2.add_node() self.assertTrue(gtn.equal(g1, g2)) # Different arc label g1 = gtn.Graph() g1.add_node(True) g1.add_node() g1.add_arc(0, 1, 0) g2 = gtn.Graph() g2.add_node(True) g2.add_node() g2.add_arc(0, 1, 1) self.assertFalse(gtn.equal(g1, g2)) # Different arc weight g1 = gtn.Graph() g1.add_node(True) g1.add_node() g1.add_arc(0, 1, 0, 0, 1.2) g2 = gtn.Graph() g2.add_node(True) g2.add_node() g2.add_arc(0, 1, 0, 0, 2.2) self.assertFalse(gtn.equal(g1, g2)) # Self loop in g1 g1 = gtn.Graph() g1.add_node(True) g1.add_node() g1.add_node(False, True) g1.add_arc(0, 1, 0) g1.add_arc(0, 1, 1) g1.add_arc(1, 1, 1) g1.add_arc(1, 2, 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(0, 1, 1) g2.add_arc(1, 2, 2) self.assertFalse(gtn.equal(g1, g2)) # Equals g1 = gtn.Graph() g1.add_node(True) g1.add_node() g1.add_node(False, True) g1.add_arc(0, 1, 0, 0, 2.1) g1.add_arc(0, 1, 1, 1, 3.1) g1.add_arc(1, 1, 1, 1, 4.1) g1.add_arc(1, 2, 2, 2, 5.1) g2 = gtn.Graph() g2.add_node(True) g2.add_node() g2.add_node(False, True) g2.add_arc(0, 1, 0, 0, 2.1) g2.add_arc(0, 1, 1, 1, 3.1) g2.add_arc(1, 1, 1, 1, 4.1) g2.add_arc(1, 2, 2, 2, 5.1) self.assertTrue(gtn.equal(g1, g2)) # Different arc order g1 = gtn.Graph() g1.add_node(True) g1.add_node() g1.add_node(False, True) g1.add_arc(0, 1, 1, 1, 3.1) g1.add_arc(0, 1, 0, 0, 2.1) g1.add_arc(1, 1, 1, 1, 4.1) g1.add_arc(1, 2, 2, 2, 5.1) g2 = gtn.Graph() g2.add_node(True) g2.add_node() g2.add_node(False, True) g2.add_arc(0, 1, 0, 0, 2.1) g2.add_arc(0, 1, 1, 1, 3.1) g2.add_arc(1, 2, 2, 2, 5.1) g2.add_arc(1, 1, 1, 1, 4.1) self.assertTrue(gtn.equal(g1, g2)) # Repeat arcs g1 = gtn.Graph() g1.add_node(True) g1.add_node(False, True) g1.add_arc(0, 1, 1, 1, 3.1) g1.add_arc(0, 1, 1, 1, 3.1) g1.add_arc(0, 1, 1, 1, 4.1) g2 = gtn.Graph() g2.add_node(True) g2.add_node(False, True) g2.add_arc(0, 1, 1, 1, 3.1) g2.add_arc(0, 1, 1, 1, 4.1) g2.add_arc(0, 1, 1, 1, 4.1) self.assertFalse(gtn.equal(g1, g2)) # Transducer with different outputs g1 = gtn.Graph() g1.add_node(True) g1.add_node(False, True) g1.add_arc(0, 1, 0, 1, 2.1) g1.add_arc(1, 1, 1, 3, 4.1) g2 = gtn.Graph() g2.add_node(True) g2.add_node(False, True) g2.add_arc(0, 1, 0, 1, 2.1) g2.add_arc(1, 1, 1, 4, 4.1) self.assertFalse(gtn.equal(g1, g2))
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))