class TestUndirectedGraphTriangulation(unittest.TestCase): def setUp(self): self.graph = MarkovModel() def test_check_clique(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'a')]) self.assertTrue(self.graph.check_clique(['a', 'b', 'c'])) def test_is_triangulated(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'a')]) self.assertTrue(self.graph.is_triangulated()) def test_triangulation_h1_inplace(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6)) phi2 = DiscreteFactor(['b', 'c'], [3, 4], np.random.rand(12)) phi3 = DiscreteFactor(['c', 'd'], [4, 5], np.random.rand(20)) phi4 = DiscreteFactor(['d', 'a'], [5, 2], np.random.random(10)) self.graph.add_factors(phi1, phi2, phi3, phi4) self.graph.triangulate(heuristic='H1', inplace=True) self.assertTrue(self.graph.is_triangulated()) self.assertListEqual( hf.recursive_sorted(self.graph.edges()), [['a', 'b'], ['a', 'c'], ['a', 'd'], ['b', 'c'], ['c', 'd']]) def test_triangulation_h2_inplace(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6)) phi2 = DiscreteFactor(['b', 'c'], [3, 4], np.random.rand(12)) phi3 = DiscreteFactor(['c', 'd'], [4, 5], np.random.rand(20)) phi4 = DiscreteFactor(['d', 'a'], [5, 2], np.random.random(10)) self.graph.add_factors(phi1, phi2, phi3, phi4) self.graph.triangulate(heuristic='H2', inplace=True) self.assertTrue(self.graph.is_triangulated()) self.assertListEqual( hf.recursive_sorted(self.graph.edges()), [['a', 'b'], ['a', 'c'], ['a', 'd'], ['b', 'c'], ['c', 'd']]) def test_triangulation_h3_inplace(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6)) phi2 = DiscreteFactor(['b', 'c'], [3, 4], np.random.rand(12)) phi3 = DiscreteFactor(['c', 'd'], [4, 5], np.random.rand(20)) phi4 = DiscreteFactor(['d', 'a'], [5, 2], np.random.random(10)) self.graph.add_factors(phi1, phi2, phi3, phi4) self.graph.triangulate(heuristic='H3', inplace=True) self.assertTrue(self.graph.is_triangulated()) self.assertListEqual( hf.recursive_sorted(self.graph.edges()), [['a', 'b'], ['a', 'd'], ['b', 'c'], ['b', 'd'], ['c', 'd']]) def test_triangulation_h4_inplace(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6)) phi2 = DiscreteFactor(['b', 'c'], [3, 4], np.random.rand(12)) phi3 = DiscreteFactor(['c', 'd'], [4, 5], np.random.rand(20)) phi4 = DiscreteFactor(['d', 'a'], [5, 2], np.random.random(10)) self.graph.add_factors(phi1, phi2, phi3, phi4) self.graph.triangulate(heuristic='H4', inplace=True) self.assertTrue(self.graph.is_triangulated()) self.assertListEqual( hf.recursive_sorted(self.graph.edges()), [['a', 'b'], ['a', 'd'], ['b', 'c'], ['b', 'd'], ['c', 'd']]) def test_triangulation_h5_inplace(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6)) phi2 = DiscreteFactor(['b', 'c'], [3, 4], np.random.rand(12)) phi3 = DiscreteFactor(['c', 'd'], [4, 5], np.random.rand(20)) phi4 = DiscreteFactor(['d', 'a'], [5, 2], np.random.random(10)) self.graph.add_factors(phi1, phi2, phi3, phi4) self.graph.triangulate(heuristic='H4', inplace=True) self.assertTrue(self.graph.is_triangulated()) self.assertListEqual( hf.recursive_sorted(self.graph.edges()), [['a', 'b'], ['a', 'd'], ['b', 'c'], ['b', 'd'], ['c', 'd']]) def test_triangulation_h6_inplace(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6)) phi2 = DiscreteFactor(['b', 'c'], [3, 4], np.random.rand(12)) phi3 = DiscreteFactor(['c', 'd'], [4, 5], np.random.rand(20)) phi4 = DiscreteFactor(['d', 'a'], [5, 2], np.random.random(10)) self.graph.add_factors(phi1, phi2, phi3, phi4) self.graph.triangulate(heuristic='H4', inplace=True) self.assertTrue(self.graph.is_triangulated()) self.assertListEqual( hf.recursive_sorted(self.graph.edges()), [['a', 'b'], ['a', 'd'], ['b', 'c'], ['b', 'd'], ['c', 'd']]) def test_cardinality_mismatch_raises_error(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) factor_list = [ DiscreteFactor(edge, [2, 2], np.random.rand(4)) for edge in self.graph.edges() ] self.graph.add_factors(*factor_list) self.graph.add_factors( DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6))) self.assertRaises(ValueError, self.graph.triangulate) def test_triangulation_h1_create_new(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6)) phi2 = DiscreteFactor(['b', 'c'], [3, 4], np.random.rand(12)) phi3 = DiscreteFactor(['c', 'd'], [4, 5], np.random.rand(20)) phi4 = DiscreteFactor(['d', 'a'], [5, 2], np.random.random(10)) self.graph.add_factors(phi1, phi2, phi3, phi4) H = self.graph.triangulate(heuristic='H1', inplace=True) self.assertListEqual( hf.recursive_sorted(H.edges()), [['a', 'b'], ['a', 'c'], ['a', 'd'], ['b', 'c'], ['c', 'd']]) def test_triangulation_h2_create_new(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6)) phi2 = DiscreteFactor(['b', 'c'], [3, 4], np.random.rand(12)) phi3 = DiscreteFactor(['c', 'd'], [4, 5], np.random.rand(20)) phi4 = DiscreteFactor(['d', 'a'], [5, 2], np.random.random(10)) self.graph.add_factors(phi1, phi2, phi3, phi4) H = self.graph.triangulate(heuristic='H2', inplace=True) self.assertListEqual( hf.recursive_sorted(H.edges()), [['a', 'b'], ['a', 'c'], ['a', 'd'], ['b', 'c'], ['c', 'd']]) def test_triangulation_h3_create_new(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6)) phi2 = DiscreteFactor(['b', 'c'], [3, 4], np.random.rand(12)) phi3 = DiscreteFactor(['c', 'd'], [4, 5], np.random.rand(20)) phi4 = DiscreteFactor(['d', 'a'], [5, 2], np.random.random(10)) self.graph.add_factors(phi1, phi2, phi3, phi4) H = self.graph.triangulate(heuristic='H3', inplace=True) self.assertListEqual( hf.recursive_sorted(H.edges()), [['a', 'b'], ['a', 'd'], ['b', 'c'], ['b', 'd'], ['c', 'd']]) def test_triangulation_h4_create_new(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6)) phi2 = DiscreteFactor(['b', 'c'], [3, 4], np.random.rand(12)) phi3 = DiscreteFactor(['c', 'd'], [4, 5], np.random.rand(20)) phi4 = DiscreteFactor(['d', 'a'], [5, 2], np.random.random(10)) self.graph.add_factors(phi1, phi2, phi3, phi4) H = self.graph.triangulate(heuristic='H4', inplace=True) self.assertListEqual( hf.recursive_sorted(H.edges()), [['a', 'b'], ['a', 'd'], ['b', 'c'], ['b', 'd'], ['c', 'd']]) def test_triangulation_h5_create_new(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6)) phi2 = DiscreteFactor(['b', 'c'], [3, 4], np.random.rand(12)) phi3 = DiscreteFactor(['c', 'd'], [4, 5], np.random.rand(20)) phi4 = DiscreteFactor(['d', 'a'], [5, 2], np.random.random(10)) self.graph.add_factors(phi1, phi2, phi3, phi4) H = self.graph.triangulate(heuristic='H5', inplace=True) self.assertListEqual( hf.recursive_sorted(H.edges()), [['a', 'b'], ['a', 'd'], ['b', 'c'], ['b', 'd'], ['c', 'd']]) def test_triangulation_h6_create_new(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6)) phi2 = DiscreteFactor(['b', 'c'], [3, 4], np.random.rand(12)) phi3 = DiscreteFactor(['c', 'd'], [4, 5], np.random.rand(20)) phi4 = DiscreteFactor(['d', 'a'], [5, 2], np.random.random(10)) self.graph.add_factors(phi1, phi2, phi3, phi4) H = self.graph.triangulate(heuristic='H6', inplace=True) self.assertListEqual( hf.recursive_sorted(H.edges()), [['a', 'b'], ['a', 'd'], ['b', 'c'], ['b', 'd'], ['c', 'd']]) def test_copy(self): # Setup the original graph self.graph.add_nodes_from(['a', 'b']) self.graph.add_edges_from([('a', 'b')]) # Generate the copy copy = self.graph.copy() # Ensure the copied model is correct self.assertTrue(copy.check_model()) # Basic sanity checks to ensure the graph was copied correctly self.assertEqual(len(copy.nodes()), 2) self.assertListEqual(copy.neighbors('a'), ['b']) self.assertListEqual(copy.neighbors('b'), ['a']) # Modify the original graph ... self.graph.add_nodes_from(['c']) self.graph.add_edges_from([('c', 'b')]) # ... and ensure none of those changes get propagated self.assertEqual(len(copy.nodes()), 2) self.assertListEqual(copy.neighbors('a'), ['b']) self.assertListEqual(copy.neighbors('b'), ['a']) with self.assertRaises(nx.NetworkXError): copy.neighbors('c') # Ensure the copy has no factors at this point self.assertEqual(len(copy.get_factors()), 0) # Add factors to the original graph phi1 = DiscreteFactor(['a', 'b'], [2, 2], [[0.3, 0.7], [0.9, 0.1]]) self.graph.add_factors(phi1) # The factors should not get copied over with self.assertRaises(AssertionError): self.assertListEqual(copy.get_factors(), self.graph.get_factors()) # Create a fresh copy del copy copy = self.graph.copy() self.assertListEqual(copy.get_factors(), self.graph.get_factors()) # If we change factors in the original, it should not be passed to the clone phi1.values = np.array([[0.5, 0.5], [0.5, 0.5]]) self.assertNotEqual(self.graph.get_factors(), copy.get_factors()) # Start with a fresh copy del copy self.graph.add_nodes_from(['d']) copy = self.graph.copy() # Ensure an unconnected node gets copied over as well self.assertEqual(len(copy.nodes()), 4) self.assertListEqual(self.graph.neighbors('a'), ['b']) self.assertTrue('a' in self.graph.neighbors('b')) self.assertTrue('c' in self.graph.neighbors('b')) self.assertListEqual(self.graph.neighbors('c'), ['b']) self.assertListEqual(self.graph.neighbors('d'), []) # Verify that changing the copied model should not update the original copy.add_nodes_from(['e']) self.assertListEqual(copy.neighbors('e'), []) with self.assertRaises(nx.NetworkXError): self.graph.neighbors('e') # Verify that changing edges in the copy doesn't create edges in the original copy.add_edges_from([('d', 'b')]) self.assertTrue('a' in copy.neighbors('b')) self.assertTrue('c' in copy.neighbors('b')) self.assertTrue('d' in copy.neighbors('b')) self.assertTrue('a' in self.graph.neighbors('b')) self.assertTrue('c' in self.graph.neighbors('b')) self.assertFalse('d' in self.graph.neighbors('b')) # If we remove factors from the copied model, it should not reflect in the original copy.remove_factors(phi1) self.assertEqual(len(self.graph.get_factors()), 1) self.assertEqual(len(copy.get_factors()), 0) def tearDown(self): del self.graph
class TestMarkovModelMethods(unittest.TestCase): def setUp(self): self.graph = MarkovModel() def test_get_cardinality(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) self.assertDictEqual(self.graph.get_cardinality(), {}) phi1 = DiscreteFactor(['a', 'b'], [1, 2], np.random.rand(2)) self.graph.add_factors(phi1) self.assertDictEqual(self.graph.get_cardinality(), {'a': 1, 'b': 2}) self.graph.remove_factors(phi1) self.assertDictEqual(self.graph.get_cardinality(), {}) phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4)) phi2 = DiscreteFactor(['c', 'd'], [1, 2], np.random.rand(2)) self.graph.add_factors(phi1, phi2) self.assertDictEqual(self.graph.get_cardinality(), { 'd': 2, 'a': 2, 'b': 2, 'c': 1 }) phi3 = DiscreteFactor(['d', 'a'], [1, 2], np.random.rand(2)) self.graph.add_factors(phi3) self.assertDictEqual(self.graph.get_cardinality(), { 'd': 1, 'c': 1, 'b': 2, 'a': 2 }) self.graph.remove_factors(phi1, phi2, phi3) self.assertDictEqual(self.graph.get_cardinality(), {}) def test_get_cardinality_check_cardinality(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [1, 2], np.random.rand(2)) self.graph.add_factors(phi1) self.assertRaises(ValueError, self.graph.get_cardinality, check_cardinality=True) phi2 = DiscreteFactor(['a', 'c'], [1, 2], np.random.rand(2)) self.graph.add_factors(phi2) self.assertRaises(ValueError, self.graph.get_cardinality, check_cardinality=True) phi3 = DiscreteFactor(['c', 'd'], [2, 2], np.random.rand(4)) self.graph.add_factors(phi3) self.assertDictEqual( self.graph.get_cardinality(check_cardinality=True), { 'd': 2, 'c': 2, 'b': 2, 'a': 1 }) def test_check_model(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [1, 2], np.random.rand(2)) phi2 = DiscreteFactor(['c', 'b'], [3, 2], np.random.rand(6)) phi3 = DiscreteFactor(['c', 'd'], [3, 4], np.random.rand(12)) phi4 = DiscreteFactor(['d', 'a'], [4, 1], np.random.rand(4)) self.graph.add_factors(phi1, phi2, phi3, phi4) self.assertTrue(self.graph.check_model()) self.graph.remove_factors(phi1, phi4) phi1 = DiscreteFactor(['a', 'b'], [4, 2], np.random.rand(8)) self.graph.add_factors(phi1) self.assertTrue(self.graph.check_model()) def test_check_model1(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [1, 2], np.random.rand(2)) phi2 = DiscreteFactor(['b', 'c'], [3, 3], np.random.rand(9)) self.graph.add_factors(phi1, phi2) self.assertRaises(ValueError, self.graph.check_model) self.graph.remove_factors(phi2) phi3 = DiscreteFactor(['c', 'a'], [4, 4], np.random.rand(16)) self.graph.add_factors(phi3) self.assertRaises(ValueError, self.graph.check_model) self.graph.remove_factors(phi3) phi2 = DiscreteFactor(['b', 'c'], [2, 3], np.random.rand(6)) phi3 = DiscreteFactor(['c', 'd'], [3, 4], np.random.rand(12)) phi4 = DiscreteFactor(['d', 'a'], [4, 3], np.random.rand(12)) self.graph.add_factors(phi2, phi3, phi4) self.assertRaises(ValueError, self.graph.check_model) self.graph.remove_factors(phi2, phi3, phi4) phi2 = DiscreteFactor(['a', 'b'], [1, 3], np.random.rand(3)) self.graph.add_factors(phi1, phi2) self.assertRaises(ValueError, self.graph.check_model) self.graph.remove_factors(phi2) def test_check_model2(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'c'], [1, 2], np.random.rand(2)) self.graph.add_factors(phi1) self.assertRaises(ValueError, self.graph.check_model) self.graph.remove_factors(phi1) phi1 = DiscreteFactor(['a', 'b'], [1, 2], np.random.rand(2)) phi2 = DiscreteFactor(['a', 'c'], [1, 2], np.random.rand(2)) self.graph.add_factors(phi1, phi2) self.assertRaises(ValueError, self.graph.check_model) self.graph.remove_factors(phi1, phi2) phi1 = DiscreteFactor(['a', 'b'], [1, 2], np.random.rand(2)) phi2 = DiscreteFactor(['b', 'c'], [2, 3], np.random.rand(6)) phi3 = DiscreteFactor(['c', 'd'], [3, 4], np.random.rand(12)) phi4 = DiscreteFactor(['d', 'a'], [4, 1], np.random.rand(4)) phi5 = DiscreteFactor(['d', 'b'], [4, 2], np.random.rand(8)) self.graph.add_factors(phi1, phi2, phi3, phi4, phi5) self.assertRaises(ValueError, self.graph.check_model) self.graph.remove_factors(phi1, phi2, phi3, phi4, phi5) def test_factor_graph(self): phi1 = DiscreteFactor(['Alice', 'Bob'], [3, 2], np.random.rand(6)) phi2 = DiscreteFactor(['Bob', 'Charles'], [2, 2], np.random.rand(4)) self.graph.add_edges_from([('Alice', 'Bob'), ('Bob', 'Charles')]) self.graph.add_factors(phi1, phi2) factor_graph = self.graph.to_factor_graph() self.assertIsInstance(factor_graph, FactorGraph) self.assertListEqual( sorted(factor_graph.nodes()), ['Alice', 'Bob', 'Charles', 'phi_Alice_Bob', 'phi_Bob_Charles']) self.assertListEqual( hf.recursive_sorted(factor_graph.edges()), [['Alice', 'phi_Alice_Bob'], ['Bob', 'phi_Alice_Bob'], ['Bob', 'phi_Bob_Charles'], ['Charles', 'phi_Bob_Charles']]) self.assertListEqual(factor_graph.get_factors(), [phi1, phi2]) def test_factor_graph_raises_error(self): self.graph.add_edges_from([('Alice', 'Bob'), ('Bob', 'Charles')]) self.assertRaises(ValueError, self.graph.to_factor_graph) def test_junction_tree(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6)) phi2 = DiscreteFactor(['b', 'c'], [3, 4], np.random.rand(12)) phi3 = DiscreteFactor(['c', 'd'], [4, 5], np.random.rand(20)) phi4 = DiscreteFactor(['d', 'a'], [5, 2], np.random.random(10)) self.graph.add_factors(phi1, phi2, phi3, phi4) junction_tree = self.graph.to_junction_tree() self.assertListEqual(hf.recursive_sorted(junction_tree.nodes()), [['a', 'b', 'd'], ['b', 'c', 'd']]) self.assertEqual(len(junction_tree.edges()), 1) def test_junction_tree_single_clique(self): self.graph.add_edges_from([('x1', 'x2'), ('x2', 'x3'), ('x1', 'x3')]) phi = [ DiscreteFactor(edge, [2, 2], np.random.rand(4)) for edge in self.graph.edges() ] self.graph.add_factors(*phi) junction_tree = self.graph.to_junction_tree() self.assertListEqual(hf.recursive_sorted(junction_tree.nodes()), [['x1', 'x2', 'x3']]) factors = junction_tree.get_factors() self.assertEqual(factors[0], factor_product(*phi)) def test_markov_blanket(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c')]) self.assertListEqual(self.graph.markov_blanket('a'), ['b']) self.assertListEqual(sorted(self.graph.markov_blanket('b')), ['a', 'c']) def test_local_independencies(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c')]) independencies = self.graph.get_local_independencies() self.assertIsInstance(independencies, Independencies) self.assertEqual(independencies, Independencies(['a', 'c', 'b'])) def test_bayesian_model(self): self.graph.add_edges_from([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')]) phi1 = DiscreteFactor(['a', 'b'], [2, 3], np.random.rand(6)) phi2 = DiscreteFactor(['b', 'c'], [3, 4], np.random.rand(12)) phi3 = DiscreteFactor(['c', 'd'], [4, 5], np.random.rand(20)) phi4 = DiscreteFactor(['d', 'a'], [5, 2], np.random.random(10)) self.graph.add_factors(phi1, phi2, phi3, phi4) bm = self.graph.to_bayesian_model() self.assertIsInstance(bm, BayesianModel) self.assertListEqual(sorted(bm.nodes()), ['a', 'b', 'c', 'd']) self.assertTrue(nx.is_chordal(bm.to_undirected())) def tearDown(self): del self.graph
class TestUndirectedGraphFactorOperations(unittest.TestCase): def setUp(self): self.graph = MarkovModel() def test_add_factor_raises_error(self): self.graph.add_edges_from([('Alice', 'Bob'), ('Bob', 'Charles'), ('Charles', 'Debbie'), ('Debbie', 'Alice')]) factor = DiscreteFactor(['Alice', 'Bob', 'John'], [2, 2, 2], np.random.rand(8)) self.assertRaises(ValueError, self.graph.add_factors, factor) def test_add_single_factor(self): self.graph.add_nodes_from(['a', 'b', 'c']) phi = DiscreteFactor(['a', 'b'], [2, 2], range(4)) self.graph.add_factors(phi) six.assertCountEqual(self, self.graph.factors, [phi]) def test_add_multiple_factors(self): self.graph.add_nodes_from(['a', 'b', 'c']) phi1 = DiscreteFactor(['a', 'b'], [2, 2], range(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], range(4)) self.graph.add_factors(phi1, phi2) six.assertCountEqual(self, self.graph.factors, [phi1, phi2]) def test_get_factors(self): self.graph.add_nodes_from(['a', 'b', 'c']) phi1 = DiscreteFactor(['a', 'b'], [2, 2], range(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], range(4)) six.assertCountEqual(self, self.graph.get_factors(), []) self.graph.add_factors(phi1, phi2) six.assertCountEqual(self, self.graph.get_factors(), [phi1, phi2]) def test_remove_single_factor(self): self.graph.add_nodes_from(['a', 'b', 'c']) phi1 = DiscreteFactor(['a', 'b'], [2, 2], range(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], range(4)) self.graph.add_factors(phi1, phi2) self.graph.remove_factors(phi1) six.assertCountEqual(self, self.graph.factors, [phi2]) def test_remove_multiple_factors(self): self.graph.add_nodes_from(['a', 'b', 'c']) phi1 = DiscreteFactor(['a', 'b'], [2, 2], range(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], range(4)) self.graph.add_factors(phi1, phi2) self.graph.remove_factors(phi1, phi2) six.assertCountEqual(self, self.graph.factors, []) def test_partition_function(self): self.graph.add_nodes_from(['a', 'b', 'c']) phi1 = DiscreteFactor(['a', 'b'], [2, 2], range(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], range(4)) self.graph.add_factors(phi1, phi2) self.graph.add_edges_from([('a', 'b'), ('b', 'c')]) self.assertEqual(self.graph.get_partition_function(), 22.0) def test_partition_function_raises_error(self): self.graph.add_nodes_from(['a', 'b', 'c', 'd']) phi1 = DiscreteFactor(['a', 'b'], [2, 2], range(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], range(4)) self.graph.add_factors(phi1, phi2) self.assertRaises(ValueError, self.graph.get_partition_function) def tearDown(self): del self.graph
class TestGibbsSampling(unittest.TestCase): def setUp(self): # A test Bayesian model diff_cpd = TabularCPD('diff', 2, [[0.6], [0.4]]) intel_cpd = TabularCPD('intel', 2, [[0.7], [0.3]]) grade_cpd = TabularCPD('grade', 3, [[0.3, 0.05, 0.9, 0.5], [0.4, 0.25, 0.08, 0.3], [0.3, 0.7, 0.02, 0.2]], evidence=['diff', 'intel'], evidence_card=[2, 2]) self.bayesian_model = BayesianModel() self.bayesian_model.add_nodes_from(['diff', 'intel', 'grade']) self.bayesian_model.add_edges_from([('diff', 'grade'), ('intel', 'grade')]) self.bayesian_model.add_cpds(diff_cpd, intel_cpd, grade_cpd) # A test Markov model self.markov_model = MarkovModel([('A', 'B'), ('C', 'B'), ('B', 'D')]) factor_ab = DiscreteFactor(['A', 'B'], [2, 3], [1, 2, 3, 4, 5, 6]) factor_cb = DiscreteFactor(['C', 'B'], [4, 3], [3, 1, 4, 5, 7, 8, 1, 3, 10, 4, 5, 6]) factor_bd = DiscreteFactor(['B', 'D'], [3, 2], [5, 7, 2, 1, 9, 3]) self.markov_model.add_factors(factor_ab, factor_cb, factor_bd) self.gibbs = GibbsSampling(self.bayesian_model) def tearDown(self): del self.bayesian_model del self.markov_model @patch('pgm.sampling.GibbsSampling._get_kernel_from_bayesian_model', autospec=True) @patch('pgm.models.MarkovChain.__init__', autospec=True) def test_init_bayesian_model(self, init, get_kernel): model = MagicMock(spec_set=BayesianModel) gibbs = GibbsSampling(model) init.assert_called_once_with(gibbs) get_kernel.assert_called_once_with(gibbs, model) @patch('pgm.sampling.GibbsSampling._get_kernel_from_markov_model', autospec=True) def test_init_markov_model(self, get_kernel): model = MagicMock(spec_set=MarkovModel) gibbs = GibbsSampling(model) get_kernel.assert_called_once_with(gibbs, model) def test_get_kernel_from_bayesian_model(self): gibbs = GibbsSampling() gibbs._get_kernel_from_bayesian_model(self.bayesian_model) self.assertListEqual(list(gibbs.variables), self.bayesian_model.nodes()) self.assertDictEqual(gibbs.cardinalities, { 'diff': 2, 'intel': 2, 'grade': 3 }) def test_get_kernel_from_markov_model(self): gibbs = GibbsSampling() gibbs._get_kernel_from_markov_model(self.markov_model) self.assertListEqual(list(gibbs.variables), self.markov_model.nodes()) self.assertDictEqual(gibbs.cardinalities, { 'A': 2, 'B': 3, 'C': 4, 'D': 2 }) def test_sample(self): start_state = [State('diff', 0), State('intel', 0), State('grade', 0)] sample = self.gibbs.sample(start_state, 2) self.assertEquals(len(sample), 2) self.assertEquals(len(sample.columns), 3) self.assertIn('diff', sample.columns) self.assertIn('intel', sample.columns) self.assertIn('grade', sample.columns) self.assertTrue(set(sample['diff']).issubset({0, 1})) self.assertTrue(set(sample['intel']).issubset({0, 1})) self.assertTrue(set(sample['grade']).issubset({0, 1, 2})) @patch("pgm.sampling.GibbsSampling.random_state", autospec=True) def test_sample_less_arg(self, random_state): self.gibbs.state = None random_state.return_value = [ State('diff', 0), State('intel', 0), State('grade', 0) ] sample = self.gibbs.sample(size=2) random_state.assert_called_once_with(self.gibbs) self.assertEqual(len(sample), 2) def test_generate_sample(self): start_state = [State('diff', 0), State('intel', 0), State('grade', 0)] gen = self.gibbs.generate_sample(start_state, 2) samples = [sample for sample in gen] self.assertEqual(len(samples), 2) self.assertEqual( {samples[0][0].var, samples[0][1].var, samples[0][2].var}, {'diff', 'intel', 'grade'}) self.assertEqual( {samples[1][0].var, samples[1][1].var, samples[1][2].var}, {'diff', 'intel', 'grade'}) @patch("pgm.sampling.GibbsSampling.random_state", autospec=True) def test_generate_sample_less_arg(self, random_state): self.gibbs.state = None gen = self.gibbs.generate_sample(size=2) samples = [sample for sample in gen] random_state.assert_called_once_with(self.gibbs) self.assertEqual(len(samples), 2)
class TestUAIWriter(unittest.TestCase): def setUp(self): self.maxDiff = None edges = [['family-out', 'dog-out'], ['bowel-problem', 'dog-out'], ['family-out', 'light-on'], ['dog-out', 'hear-bark']] cpds = {'bowel-problem': np.array([[0.01], [0.99]]), 'dog-out': np.array([[0.99, 0.01, 0.97, 0.03], [0.9, 0.1, 0.3, 0.7]]), 'family-out': np.array([[0.15], [0.85]]), 'hear-bark': np.array([[0.7, 0.3], [0.01, 0.99]]), 'light-on': np.array([[0.6, 0.4], [0.05, 0.95]])} states = {'bowel-problem': ['true', 'false'], 'dog-out': ['true', 'false'], 'family-out': ['true', 'false'], 'hear-bark': ['true', 'false'], 'light-on': ['true', 'false']} parents = {'bowel-problem': [], 'dog-out': ['bowel-problem', 'family-out'], 'family-out': [], 'hear-bark': ['dog-out'], 'light-on': ['family-out']} self.bayesmodel = BayesianModel(edges) tabular_cpds = [] for var, values in cpds.items(): cpd = TabularCPD(var, len(states[var]), values, evidence=parents[var], evidence_card=[len(states[evidence_var]) for evidence_var in parents[var]]) tabular_cpds.append(cpd) self.bayesmodel.add_cpds(*tabular_cpds) self.bayeswriter = UAIWriter(self.bayesmodel) edges = {('var_0', 'var_1'), ('var_0', 'var_2'), ('var_1', 'var_2')} self.markovmodel = MarkovModel(edges) tables = [(['var_0', 'var_1'], ['4.000', '2.400', '1.000', '0.000']), (['var_0', 'var_1', 'var_2'], ['2.2500', '3.2500', '3.7500', '0.0000', '0.0000', '10.0000', '1.8750', '4.0000', '3.3330', '2.0000', '2.0000', '3.4000'])] domain = {'var_1': '2', 'var_2': '3', 'var_0': '2'} factors = [] for table in tables: variables = table[0] cardinality = [int(domain[var]) for var in variables] values = list(map(float, table[1])) factor = DiscreteFactor(variables, cardinality, values) factors.append(factor) self.markovmodel.add_factors(*factors) self.markovwriter = UAIWriter(self.markovmodel) def test_bayes_model(self): self.expected_bayes_file = """BAYES 5 2 2 2 2 2 5 1 0 3 2 0 1 1 2 2 1 3 2 2 4 2 0.01 0.99 8 0.99 0.01 0.97 0.03 0.9 0.1 0.3 0.7 2 0.15 0.85 4 0.7 0.3 0.01 0.99 4 0.6 0.4 0.05 0.95""" self.assertEqual(str(self.bayeswriter.__str__()), str(self.expected_bayes_file)) def test_markov_model(self): self.expected_markov_file = """MARKOV 3 2 2 3 2 2 0 1 3 0 1 2 4 4.0 2.4 1.0 0.0 12 2.25 3.25 3.75 0.0 0.0 10.0 1.875 4.0 3.333 2.0 2.0 3.4""" self.assertEqual(str(self.markovwriter.__str__()), str(self.expected_markov_file))
class TestVariableEliminationMarkov(unittest.TestCase): def setUp(self): # It is just a moralised version of the above Bayesian network so all the results are same. Only factors # are under consideration for inference so this should be fine. self.markov_model = MarkovModel([('A', 'J'), ('R', 'J'), ('J', 'Q'), ('J', 'L'), ('G', 'L'), ('A', 'R'), ('J', 'G')]) factor_a = TabularCPD('A', 2, values=[[0.2], [0.8]]).to_factor() factor_r = TabularCPD('R', 2, values=[[0.4], [0.6]]).to_factor() factor_j = TabularCPD('J', 2, values=[[0.9, 0.6, 0.7, 0.1], [0.1, 0.4, 0.3, 0.9]], evidence=['A', 'R'], evidence_card=[2, 2]).to_factor() factor_q = TabularCPD('Q', 2, values=[[0.9, 0.2], [0.1, 0.8]], evidence=['J'], evidence_card=[2]).to_factor() factor_l = TabularCPD('L', 2, values=[[0.9, 0.45, 0.8, 0.1], [0.1, 0.55, 0.2, 0.9]], evidence=['J', 'G'], evidence_card=[2, 2]).to_factor() factor_g = TabularCPD('G', 2, [[0.6], [0.4]]).to_factor() self.markov_model.add_factors(factor_a, factor_r, factor_j, factor_q, factor_l, factor_g) self.markov_inference = VariableElimination(self.markov_model) # All the values that are used for comparision in the all the tests are # found using SAMIAM (assuming that it is correct ;)) def test_query_single_variable(self): query_result = self.markov_inference.query(['J']) np_test.assert_array_almost_equal(query_result['J'].values, np.array([0.416, 0.584])) def test_query_multiple_variable(self): query_result = self.markov_inference.query(['Q', 'J']) np_test.assert_array_almost_equal(query_result['J'].values, np.array([0.416, 0.584])) np_test.assert_array_almost_equal(query_result['Q'].values, np.array([0.4912, 0.5088])) def test_query_single_variable_with_evidence(self): query_result = self.markov_inference.query(variables=['J'], evidence={ 'A': 0, 'R': 1 }) np_test.assert_array_almost_equal(query_result['J'].values, np.array([0.60, 0.40])) def test_query_multiple_variable_with_evidence(self): query_result = self.markov_inference.query(variables=['J', 'Q'], evidence={ 'A': 0, 'R': 0, 'G': 0, 'L': 1 }) np_test.assert_array_almost_equal(query_result['J'].values, np.array([0.818182, 0.181818])) np_test.assert_array_almost_equal(query_result['Q'].values, np.array([0.772727, 0.227273])) def test_query_multiple_times(self): # This just tests that the models are not getting modified while querying them query_result = self.markov_inference.query(['J']) query_result = self.markov_inference.query(['J']) np_test.assert_array_almost_equal(query_result['J'].values, np.array([0.416, 0.584])) query_result = self.markov_inference.query(['Q', 'J']) query_result = self.markov_inference.query(['Q', 'J']) np_test.assert_array_almost_equal(query_result['J'].values, np.array([0.416, 0.584])) np_test.assert_array_almost_equal(query_result['Q'].values, np.array([0.4912, 0.5088])) query_result = self.markov_inference.query(variables=['J'], evidence={ 'A': 0, 'R': 1 }) query_result = self.markov_inference.query(variables=['J'], evidence={ 'A': 0, 'R': 1 }) np_test.assert_array_almost_equal(query_result['J'].values, np.array([0.60, 0.40])) query_result = self.markov_inference.query(variables=['J', 'Q'], evidence={ 'A': 0, 'R': 0, 'G': 0, 'L': 1 }) query_result = self.markov_inference.query(variables=['J', 'Q'], evidence={ 'A': 0, 'R': 0, 'G': 0, 'L': 1 }) np_test.assert_array_almost_equal(query_result['J'].values, np.array([0.818182, 0.181818])) np_test.assert_array_almost_equal(query_result['Q'].values, np.array([0.772727, 0.227273])) def test_max_marginal(self): np_test.assert_almost_equal(self.markov_inference.max_marginal(), 0.1659, decimal=4) def test_max_marginal_var(self): np_test.assert_almost_equal(self.markov_inference.max_marginal(['G']), 0.5714, decimal=4) def test_max_marginal_var1(self): np_test.assert_almost_equal(self.markov_inference.max_marginal( ['G', 'R']), 0.4055, decimal=4) def test_max_marginal_var2(self): np_test.assert_almost_equal(self.markov_inference.max_marginal( ['G', 'R', 'A']), 0.3260, decimal=4) def test_map_query(self): map_query = self.markov_inference.map_query() self.assertDictEqual(map_query, { 'A': 1, 'R': 1, 'J': 1, 'Q': 1, 'G': 0, 'L': 0 }) def test_map_query_with_evidence(self): map_query = self.markov_inference.map_query(['A', 'R', 'L'], { 'J': 0, 'Q': 1, 'G': 0 }) self.assertDictEqual(map_query, {'A': 1, 'R': 0, 'L': 0}) def test_induced_graph(self): induced_graph = self.markov_inference.induced_graph( ['G', 'Q', 'A', 'J', 'L', 'R']) result_edges = sorted([sorted(x) for x in induced_graph.edges()]) self.assertEqual([['A', 'J'], ['A', 'R'], ['G', 'J'], ['G', 'L'], ['J', 'L'], ['J', 'Q'], ['J', 'R'], ['L', 'R']], result_edges) def test_induced_width(self): result_width = self.markov_inference.induced_width( ['G', 'Q', 'A', 'J', 'L', 'R']) self.assertEqual(2, result_width) def tearDown(self): del self.markov_inference del self.markov_model
class TestInferenceBase(unittest.TestCase): def setUp(self): self.bayesian = BayesianModel([('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'e')]) a_cpd = TabularCPD('a', 2, [[0.4, 0.6]]) b_cpd = TabularCPD('b', 2, [[0.2, 0.4], [0.8, 0.6]], evidence=['a'], evidence_card=[2]) c_cpd = TabularCPD('c', 2, [[0.1, 0.2], [0.9, 0.8]], evidence=['b'], evidence_card=[2]) d_cpd = TabularCPD('d', 2, [[0.4, 0.3], [0.6, 0.7]], evidence=['c'], evidence_card=[2]) e_cpd = TabularCPD('e', 2, [[0.3, 0.2], [0.7, 0.8]], evidence=['d'], evidence_card=[2]) self.bayesian.add_cpds(a_cpd, b_cpd, c_cpd, d_cpd, e_cpd) self.markov = MarkovModel([('a', 'b'), ('b', 'd'), ('a', 'c'), ('c', 'd')]) factor_1 = DiscreteFactor(['a', 'b'], [2, 2], np.array([100, 1, 1, 100])) factor_2 = DiscreteFactor(['a', 'c'], [2, 2], np.array([40, 30, 100, 20])) factor_3 = DiscreteFactor(['b', 'd'], [2, 2], np.array([1, 100, 100, 1])) factor_4 = DiscreteFactor(['c', 'd'], [2, 2], np.array([60, 60, 40, 40])) self.markov.add_factors(factor_1, factor_2, factor_3, factor_4) def test_bayesian_inference_init(self): infer_bayesian = Inference(self.bayesian) self.assertEqual(set(infer_bayesian.variables), {'a', 'b', 'c', 'd', 'e'}) self.assertEqual(infer_bayesian.cardinality, {'a': 2, 'b': 2, 'c': 2, 'd': 2, 'e': 2}) self.assertIsInstance(infer_bayesian.factors, defaultdict) self.assertEqual(set(infer_bayesian.factors['a']), set([self.bayesian.get_cpds('a').to_factor(), self.bayesian.get_cpds('b').to_factor()])) self.assertEqual(set(infer_bayesian.factors['b']), set([self.bayesian.get_cpds('b').to_factor(), self.bayesian.get_cpds('c').to_factor()])) self.assertEqual(set(infer_bayesian.factors['c']), set([self.bayesian.get_cpds('c').to_factor(), self.bayesian.get_cpds('d').to_factor()])) self.assertEqual(set(infer_bayesian.factors['d']), set([self.bayesian.get_cpds('d').to_factor(), self.bayesian.get_cpds('e').to_factor()])) self.assertEqual(set(infer_bayesian.factors['e']), set([self.bayesian.get_cpds('e').to_factor()])) def test_markov_inference_init(self): infer_markov = Inference(self.markov) self.assertEqual(set(infer_markov.variables), {'a', 'b', 'c', 'd'}) self.assertEqual(infer_markov.cardinality, {'a': 2, 'b': 2, 'c': 2, 'd': 2}) self.assertEqual(infer_markov.factors, {'a': [DiscreteFactor(['a', 'b'], [2, 2], np.array([100, 1, 1, 100])), DiscreteFactor(['a', 'c'], [2, 2], np.array([40, 30, 100, 20]))], 'b': [DiscreteFactor(['a', 'b'], [2, 2], np.array([100, 1, 1, 100])), DiscreteFactor(['b', 'd'], [2, 2], np.array([1, 100, 100, 1]))], 'c': [DiscreteFactor(['a', 'c'], [2, 2], np.array([40, 30, 100, 20])), DiscreteFactor(['c', 'd'], [2, 2], np.array([60, 60, 40, 40]))], 'd': [DiscreteFactor(['b', 'd'], [2, 2], np.array([1, 100, 100, 1])), DiscreteFactor(['c', 'd'], [2, 2], np.array([60, 60, 40, 40]))]})