class TestFactorGraphFactorOperations(unittest.TestCase): def setUp(self): self.graph = FactorGraph() def test_add_single_factor(self): self.graph.add_edges_from([('a', 'phi1'), ('b', 'phi1')]) phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4)) self.graph.add_factors(phi1) six.assertCountEqual(self, self.graph.factors, [phi1]) def test_add_multiple_factors(self): phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], np.random.rand(4)) self.graph.add_edges_from([('a', phi1), ('b', phi1), ('b', phi2), ('c', phi2)]) self.graph.add_factors(phi1, phi2) six.assertCountEqual(self, self.graph.factors, [phi1, phi2]) def test_get_factors(self): phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], np.random.rand(4)) self.graph.add_edges_from([('a', phi1), ('b', phi1), ('b', phi2), ('c', phi2)]) six.assertCountEqual(self, self.graph.get_factors(), []) self.graph.add_factors(phi1, phi2) self.assertEqual(self.graph.get_factors(node=phi1), phi1) self.assertEqual(self.graph.get_factors(node=phi2), phi2) six.assertCountEqual(self, self.graph.get_factors(), [phi1, phi2]) self.graph.remove_factors(phi1) self.assertRaises(ValueError, self.graph.get_factors, node=phi1) def test_remove_factors(self): self.graph.add_edges_from([('a', 'phi1'), ('b', 'phi1'), ('b', 'phi2'), ('c', 'phi2')]) phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], np.random.rand(4)) self.graph.add_factors(phi1, phi2) self.graph.remove_factors(phi1) six.assertCountEqual(self, self.graph.factors, [phi2]) def test_get_partition_function(self): phi1 = DiscreteFactor(['a', 'b'], [2, 2], range(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], range(4)) self.graph.add_edges_from([('a', phi1), ('b', phi1), ('b', phi2), ('c', phi2)]) self.graph.add_factors(phi1, phi2) self.assertEqual(self.graph.get_partition_function(), 22.0) def tearDown(self): del self.graph
def to_factor_graph(self): """ Converts the markov model into factor graph. A factor graph contains two types of nodes. One type corresponds to random variables whereas the second type corresponds to factors over these variables. The graph only contains edges between variables and factor nodes. Each factor node is associated with one factor whose scope is the set of variables that are its neighbors. Examples -------- >>> from pgm.models import MarkovModel >>> from pgm.factors.discrete import DiscreteFactor >>> student = MarkovModel([('Alice', 'Bob'), ('Bob', 'Charles')]) >>> factor1 = DiscreteFactor(['Alice', 'Bob'], [3, 2], np.random.rand(6)) >>> factor2 = DiscreteFactor(['Bob', 'Charles'], [2, 2], np.random.rand(4)) >>> student.add_factors(factor1, factor2) >>> factor_graph = student.to_factor_graph() """ from pgm.models import FactorGraph factor_graph = FactorGraph() if not self.factors: raise ValueError( 'Factors not associated with the random variables.') factor_graph.add_nodes_from(self.nodes()) for factor in self.factors: scope = factor.scope() factor_node = 'phi_' + '_'.join(scope) factor_graph.add_edges_from(itertools.product( scope, [factor_node])) factor_graph.add_factors(factor) return factor_graph
class TestFactorGraphMethods(unittest.TestCase): def setUp(self): self.graph = FactorGraph() def test_get_cardinality(self): self.graph.add_edges_from([('a', 'phi1'), ('b', 'phi1'), ('c', 'phi2'), ('d', 'phi2'), ('a', 'phi3'), ('d', 'phi3')]) 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_nodes_from(['a', 'b', 'c', 'd']) phi1 = DiscreteFactor(['a', 'b'], [1, 2], np.random.rand(2)) self.graph.add_factors(phi1) self.graph.add_edges_from([('a', phi1), ('b', 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.graph.add_edges_from([('a', phi2), ('c', phi2)]) self.assertRaises(ValueError, self.graph.get_cardinality, check_cardinality=True) phi3 = DiscreteFactor(['d', 'a'], [1, 1], np.random.rand(1)) self.graph.add_factors(phi3) self.graph.add_edges_from([('d', phi3), ('a', phi3)]) self.assertDictEqual( self.graph.get_cardinality(check_cardinality=True), { 'd': 1, 'c': 2, 'b': 2, 'a': 1 }) def test_get_factor_nodes(self): phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], np.random.rand(4)) self.graph.add_edges_from([('a', phi1), ('b', phi1), ('b', phi2), ('c', phi2)]) self.graph.add_factors(phi1, phi2) six.assertCountEqual(self, self.graph.get_factor_nodes(), [phi1, phi2]) def test_get_variable_nodes(self): phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], np.random.rand(4)) self.graph.add_edges_from([('a', phi1), ('b', phi1), ('b', phi2), ('c', phi2)]) self.graph.add_factors(phi1, phi2) six.assertCountEqual(self, self.graph.get_variable_nodes(), ['a', 'b', 'c']) def test_get_variable_nodes_raises_error(self): self.graph.add_edges_from([('a', 'phi1'), ('b', 'phi1'), ('b', 'phi2'), ('c', 'phi2')]) self.assertRaises(ValueError, self.graph.get_variable_nodes) def test_to_markov_model(self): phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], np.random.rand(4)) self.graph.add_edges_from([('a', phi1), ('b', phi1), ('b', phi2), ('c', phi2)]) self.graph.add_factors(phi1, phi2) mm = self.graph.to_markov_model() self.assertIsInstance(mm, MarkovModel) self.assertListEqual(sorted(mm.nodes()), ['a', 'b', 'c']) self.assertListEqual(hf.recursive_sorted(mm.edges()), [['a', 'b'], ['b', 'c']]) self.assertListEqual(sorted(mm.get_factors(), key=lambda x: x.scope()), [phi1, phi2]) def test_to_junction_tree(self): phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], np.random.rand(4)) self.graph.add_edges_from([('a', phi1), ('b', phi1), ('b', phi2), ('c', phi2)]) self.graph.add_factors(phi1, phi2) jt = self.graph.to_junction_tree() self.assertIsInstance(jt, JunctionTree) self.assertListEqual(hf.recursive_sorted(jt.nodes()), [['a', 'b'], ['b', 'c']]) self.assertEqual(len(jt.edges()), 1) def test_check_model(self): self.graph.add_nodes_from(['a', 'b', 'c']) phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], np.random.rand(4)) self.graph.add_nodes_from([phi1, phi2]) self.graph.add_edges_from([('a', phi1), ('b', phi1), ('b', phi2), ('c', phi2)]) self.graph.add_factors(phi1, phi2) self.assertTrue(self.graph.check_model()) self.graph.remove_factors(phi1) self.graph.remove_node(phi1) phi1 = DiscreteFactor(['a', 'b'], [4, 2], np.random.rand(8)) self.graph.add_factors(phi1) self.graph.add_edges_from([('a', phi1)]) self.assertTrue(self.graph.check_model()) def test_check_model1(self): self.graph.add_nodes_from(['a', 'b', 'c', 'd']) phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], np.random.rand(4)) self.graph.add_nodes_from([phi1, phi2]) self.graph.add_edges_from([('a', phi1), ('b', phi1), ('b', phi2), ('c', phi2)]) self.graph.add_factors(phi1, phi2) self.assertRaises(ValueError, self.graph.check_model) self.graph.remove_node('d') self.assertTrue(self.graph.check_model()) def test_check_model2(self): self.graph.add_nodes_from(['a', 'b', 'c']) phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], np.random.rand(4)) self.graph.add_nodes_from([phi1, phi2]) self.graph.add_edges_from([('a', phi1), ('b', phi1), ('b', phi2), ('c', phi2)]) self.graph.add_factors(phi1, phi2) self.graph.add_edges_from([('a', 'b')]) self.assertRaises(ValueError, self.graph.check_model) self.graph.add_edges_from([(phi1, phi2)]) self.assertRaises(ValueError, self.graph.check_model) self.graph.remove_edges_from([('a', 'b'), (phi1, phi2)]) self.assertTrue(self.graph.check_model()) def test_check_model3(self): self.graph.add_nodes_from(['a', 'b', 'c']) phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4)) phi2 = DiscreteFactor(['b', 'c'], [2, 2], np.random.rand(4)) phi3 = DiscreteFactor(['a', 'c'], [2, 2], np.random.rand(4)) self.graph.add_nodes_from([phi1, phi2]) self.graph.add_edges_from([('a', phi1), ('b', phi1), ('b', phi2), ('c', phi2)]) self.graph.add_factors(phi1, phi2, phi3) self.assertRaises(ValueError, self.graph.check_model) self.graph.remove_factors(phi3) self.assertTrue(self.graph.check_model()) def test_check_model4(self): self.graph.add_nodes_from(['a', 'b', 'c']) phi1 = DiscreteFactor(['a', 'b'], [2, 2], np.random.rand(4)) phi2 = DiscreteFactor(['b', 'c'], [3, 2], np.random.rand(6)) self.graph.add_nodes_from([phi1, phi2]) self.graph.add_edges_from([('a', phi1), ('b', phi1), ('b', phi2), ('c', phi2)]) self.graph.add_factors(phi1, phi2) self.assertRaises(ValueError, self.graph.check_model) self.graph.remove_factors(phi2) self.graph.remove_node(phi2) phi3 = DiscreteFactor(['c', 'a'], [4, 4], np.random.rand(16)) self.graph.add_factors(phi3) self.graph.add_edges_from([('a', phi3), ('c', phi3)]) self.assertRaises(ValueError, self.graph.check_model) def tearDown(self): del self.graph