def __init__(self, variables, pdf, *args, **kwargs): """ Parameters ---------- variables: list or array-like The variables for wich the distribution is defined. pdf: function The probability density function of the distribution. Examples -------- >>> import numpy as np >>> from scipy.special import beta >>> from pgmpy.factors.continuous import ContinuousFactor # Two variable dirichlet distribution with alpha = (1,2) >>> def dirichlet_pdf(x, y): ... return (np.power(x, 1) * np.power(y, 2)) / beta(x, y) >>> dirichlet_factor = ContinuousFactor(['x', 'y'], dirichlet_pdf) >>> dirichlet_factor.scope() ['x', 'y'] >>> dirichlet_factor.assignment(5,6) 226800.0 """ if not isinstance(variables, (list, tuple, np.ndarray)): raise TypeError( "variables: Expected type list or array-like, " "got type {var_type}".format(var_type=type(variables))) if len(set(variables)) != len(variables): raise ValueError("Variable names cannot be same.") variables = list(variables) if isinstance(pdf, str): if pdf == "gaussian": self.distribution = GaussianDistribution( variables=variables, mean=kwargs["mean"], covariance=kwargs["covariance"], ) else: raise NotImplementedError( "{dist} distribution not supported.", "Please use CustomDistribution".format(dist=pdf), ) elif isinstance(pdf, CustomDistribution): self.distribution = pdf elif callable(pdf): self.distribution = CustomDistribution(variables=variables, distribution=pdf) else: raise ValueError( "pdf: Expected type: str or function, ", "Got: {instance}".format(instance=type(variables)), )
def __init__(self, variables, pdf, *args, **kwargs): """ Parameters ---------- variables: list or array-like The variables for wich the distribution is defined. pdf: function The probability density function of the distribution. Examples -------- >>> import numpy as np >>> from scipy.special import beta >>> from pgmpy.factors.continuous import ContinuousFactor # Two variable drichlet ditribution with alpha = (1,2) >>> def drichlet_pdf(x, y): ... return (np.power(x, 1) * np.power(y, 2)) / beta(x, y) >>> dirichlet_factor = ContinuousFactor(['x', 'y'], drichlet_pdf) >>> dirichlet_factor.scope() ['x', 'y'] >>> dirichlet_factor.assignment(5,6) 226800.0 """ if not isinstance(variables, (list, tuple, np.ndarray)): raise TypeError("variables: Expected type list or array-like, " "got type {var_type}".format(var_type=type(variables))) if len(set(variables)) != len(variables): raise ValueError("Variable names cannot be same.") variables = list(variables) if isinstance(pdf, str): if pdf == 'gaussian': self.distribution = GaussianDistribution( variables=variables, mean=kwargs['mean'], covariance=kwargs['covariance']) else: raise NotImplementedError("{dist} distribution not supported.", "Please use CustomDistribution". format(dist=pdf)) elif isinstance(pdf, CustomDistribution): self.distribution = pdf elif callable(pdf): self.distribution = CustomDistribution( variables=variables, distribution=pdf) else: raise ValueError("pdf: Expected type: str or function, ", "Got: {instance}".format(instance=type(variables)))
def test_class_init(self): phi1 = CustomDistribution(["x", "y"], self.pdf1) self.assertEqual(phi1.variables, ["x", "y"]) self.assertEqual(phi1._pdf, self.pdf1) phi2 = CustomDistribution(["x1", "x2"], self.pdf2) self.assertEqual(phi2.variables, ["x1", "x2"]) self.assertEqual(phi2._pdf, self.pdf2) phi3 = CustomDistribution(["x", "y", "z"], self.pdf3) self.assertEqual(phi3.variables, ["x", "y", "z"]) self.assertEqual(phi3._pdf, self.pdf3)
def test_class_init(self): phi1 = CustomDistribution(['x', 'y'], self.pdf1) self.assertEqual(phi1.variables, ['x', 'y']) self.assertEqual(phi1._pdf, self.pdf1) phi2 = CustomDistribution(['x1', 'x2'], self.pdf2) self.assertEqual(phi2.variables, ['x1', 'x2']) self.assertEqual(phi2._pdf, self.pdf2) phi3 = CustomDistribution(['x', 'y', 'z'], self.pdf3) self.assertEqual(phi3.variables, ['x', 'y', 'z']) self.assertEqual(phi3._pdf, self.pdf3)
def test_normalize(self): def pdf2(x1, x2): return 2 * self.pdf2(x1, x2) phi2 = CustomDistribution(["x1", "x2"], pdf2) phi4 = phi2.copy() phi4.normalize() self.assertEqual(phi4.variables, phi2.variables) for inp in np.random.rand(1, 2): np_test.assert_almost_equal(phi4._pdf(inp[0], inp[1]), self.pdf2(inp[0], inp[1])) phi4 = phi2.normalize(inplace=False) self.assertEqual(phi4.variables, phi4.variables) for inp in np.random.rand(1, 2): np_test.assert_almost_equal(phi4._pdf(inp[0], inp[1]), self.pdf2(inp[0], inp[1]))
def test_normalize(self): def pdf2(x1, x2): return 2 * self.pdf2(x1, x2) phi2 = CustomDistribution(['x1', 'x2'], pdf2) phi4 = phi2.copy() phi4.normalize() self.assertEqual(phi4.variables, phi2.variables) for inp in np.random.rand(1, 2): np_test.assert_almost_equal(phi4._pdf(inp[0], inp[1]), self.pdf2(inp[0], inp[1])) phi4 = phi2.normalize(inplace=False) self.assertEqual(phi4.variables, phi4.variables) for inp in np.random.rand(1, 2): np_test.assert_almost_equal(phi4._pdf(inp[0], inp[1]), self.pdf2(inp[0], inp[1]))
def setUp(self): self.phi1 = CustomDistribution(["x", "y"], self.pdf1) self.phi2 = CustomDistribution(["x1", "x2"], self.pdf2) self.phi3 = CustomDistribution(["x", "y", "z"], self.pdf3) self.phi4 = CustomDistribution(["x1", "x2", "x3"], self.pdf4)
class TestCustomDistributionMethods(unittest.TestCase): def pdf1(self, x, y): return np.power(x, 1) * np.power(y, 2) / beta(x, y) def pdf2(self, x1, x2): return multivariate_normal.pdf([x1, x2], [0, 0], [[1, 0], [0, 1]]) def pdf3(self, x, y, z): return z * (np.power(x, 1) * np.power(y, 2)) / beta(x, y) def pdf4(self, x1, x2, x3): return multivariate_normal.pdf([x1, x2, x3], [0, 0, 0], [[1, 0, 0], [0, 1, 0], [0, 0, 1]]) def setUp(self): self.phi1 = CustomDistribution(["x", "y"], self.pdf1) self.phi2 = CustomDistribution(["x1", "x2"], self.pdf2) self.phi3 = CustomDistribution(["x", "y", "z"], self.pdf3) self.phi4 = CustomDistribution(["x1", "x2", "x3"], self.pdf4) def test_variables(self): self.assertEqual(self.phi1.variables, self.phi1._variables) self.assertEqual(self.phi2.variables, self.phi2._variables) self.assertEqual(self.phi3.variables, self.phi3._variables) def test_assignment(self): self.assertEqual(self.phi1.assignment(1.212, 2), self.pdf1(1.212, 2)) self.assertEqual(self.phi2.assignment(1, -2.231), self.pdf2(1, -2.231)) self.assertEqual(self.phi3.assignment(1.212, 2.213, -3), self.pdf3(1.212, 2.213, -3)) def test_reduce(self): phi1 = self.phi1.copy() phi1.reduce([("x", 1)]) def reduced_pdf1(y): return (np.power(1, 1) * np.power(y, 2)) / beta(1, y) self.assertEqual(phi1.variables, ["y"]) for inp in np.random.rand(4): self.assertEqual(phi1._pdf(inp), reduced_pdf1(inp)) self.assertEqual(phi1._pdf(y=inp), reduced_pdf1(inp)) phi1 = self.phi1.reduce([("x", 1)], inplace=False) self.assertEqual(phi1.variables, ["y"]) for inp in np.random.rand(4): self.assertEqual(phi1._pdf(inp), reduced_pdf1(inp)) self.assertEqual(phi1._pdf(y=inp), reduced_pdf1(inp)) phi2 = self.phi2.copy() phi2.reduce([("x2", 7.213)]) def reduced_pdf2(x1): return multivariate_normal.pdf([x1, 7.213], [0, 0], [[1, 0], [0, 1]]) self.assertEqual(phi2.variables, ["x1"]) for inp in np.random.rand(4): self.assertEqual(phi2._pdf(inp), reduced_pdf2(inp)) self.assertEqual(phi2._pdf(x1=inp), reduced_pdf2(inp)) phi2 = self.phi2.reduce([("x2", 7.213)], inplace=False) self.assertEqual(phi2.variables, ["x1"]) for inp in np.random.rand(4): self.assertEqual(phi2._pdf(inp), reduced_pdf2(inp)) self.assertEqual(phi2._pdf(x1=inp), reduced_pdf2(inp)) phi3 = self.phi3.copy() phi3.reduce([("y", 0.112), ("z", 23)]) def reduced_pdf4(x): return 23 * (np.power(x, 1) * np.power(0.112, 2)) / beta(x, 0.112) self.assertEqual(phi3.variables, ["x"]) for inp in np.random.rand(4): self.assertEqual(phi3._pdf(inp), reduced_pdf4(inp)) self.assertEqual(phi3._pdf(x=inp), reduced_pdf4(inp)) phi3 = self.phi3.copy() phi3.reduce([("y", 0.112)]) def reduced_pdf3(x, z): return z * (np.power(x, 1) * np.power(0.112, 2)) / beta(x, 0.112) self.assertEqual(phi3.variables, ["x", "z"]) for inp in np.random.rand(4, 2): self.assertEqual(phi3._pdf(inp[0], inp[1]), reduced_pdf3(inp[0], inp[1])) self.assertEqual(phi3._pdf(x=inp[0], z=inp[1]), reduced_pdf3(inp[0], inp[1])) phi3 = self.phi3.reduce([("y", 0.112)], inplace=False) self.assertEqual(phi3.variables, ["x", "z"]) for inp in np.random.rand(4, 2): self.assertEqual(phi3._pdf(inp[0], inp[1]), reduced_pdf3(inp[0], inp[1])) self.assertEqual(phi3._pdf(x=inp[0], z=inp[1]), reduced_pdf3(inp[0], inp[1])) self.assertEqual(phi3._pdf(inp[0], z=inp[1]), reduced_pdf3(inp[0], inp[1])) phi3 = self.phi3.reduce([("y", 0.112), ("z", 23)], inplace=False) self.assertEqual(phi3.variables, ["x"]) for inp in np.random.rand(4): self.assertEqual(phi3._pdf(inp), reduced_pdf4(inp)) self.assertEqual(phi3._pdf(x=inp), reduced_pdf4(inp)) def test_reduce_error(self): self.assertRaises(TypeError, self.phi1.reduce, "x1") self.assertRaises(TypeError, self.phi1.reduce, set(["x", "y"])) self.assertRaises(TypeError, self.phi1.reduce, {"x": 1, "y": 1}) self.assertRaises(TypeError, self.phi4.reduce, "x4") self.assertRaises(TypeError, self.phi4.reduce, set(["x1", "x2", "x3"])) self.assertRaises(TypeError, self.phi4.reduce, { "x1": 1, "x2": 1, "x3": 1 }) self.assertRaises(ValueError, self.phi1.reduce, [("z", 3)]) self.assertRaises(ValueError, self.phi1.reduce, [("x", 0), ("y", 1), ("z", 4)]) self.assertRaises(ValueError, self.phi4.reduce, [("x4", 7)]) self.assertRaises(ValueError, self.phi4.reduce, [("x1", 1), ("x2", 2), ("x3", 3), ("x4", 4)]) def test_marginalize(self): phi2 = self.phi2.copy() phi2.marginalize(["x2"]) self.assertEqual(phi2.variables, ["x1"]) for inp in np.random.rand(4): np_test.assert_almost_equal( phi2._pdf(inp), multivariate_normal.pdf([inp], [0], [[1]])) phi2 = self.phi2.marginalize(["x2"], inplace=False) self.assertEqual(phi2.variables, ["x1"]) for inp in np.random.rand(4): np_test.assert_almost_equal( phi2._pdf(inp), multivariate_normal.pdf([inp], [0], [[1]])) phi4 = self.phi4.copy() phi4.marginalize(["x2"]) self.assertEqual(phi4.variables, ["x1", "x3"]) for inp in np.random.rand(4, 2): np_test.assert_almost_equal( phi4._pdf(inp[0], inp[1]), multivariate_normal.pdf([inp[0], inp[1]], [0, 0], [[1, 0], [0, 1]]), ) phi4.marginalize(["x3"]) self.assertEqual(phi4.variables, ["x1"]) for inp in np.random.rand(1): np_test.assert_almost_equal( phi4._pdf(inp), multivariate_normal.pdf([inp], [0], [[1]])) phi4 = self.phi4.marginalize(["x2"], inplace=False) self.assertEqual(phi4.variables, ["x1", "x3"]) for inp in np.random.rand(4, 2): np_test.assert_almost_equal( phi4._pdf(inp[0], inp[1]), multivariate_normal.pdf([inp[0], inp[1]], [0, 0], [[1, 0], [0, 1]]), ) phi4 = phi4.marginalize(["x3"], inplace=False) self.assertEqual(phi4.variables, ["x1"]) for inp in np.random.rand(1): np_test.assert_almost_equal( phi4._pdf(inp), multivariate_normal.pdf([inp], [0], [[1]])) def test_marginalize_error(self): self.assertRaises(TypeError, self.phi1.marginalize, "x1") self.assertRaises(TypeError, self.phi1.marginalize, set(["x", "y"])) self.assertRaises(TypeError, self.phi1.marginalize, {"x": 1, "y": 1}) self.assertRaises(TypeError, self.phi4.marginalize, "x4") self.assertRaises(TypeError, self.phi4.marginalize, set(["x1", "x2", "x3"])) self.assertRaises(TypeError, self.phi4.marginalize, { "x1": 1, "x2": 1, "x3": 1 }) self.assertRaises(ValueError, self.phi1.marginalize, ["z"]) self.assertRaises(ValueError, self.phi1.marginalize, ["x", "y", "z"]) self.assertRaises(ValueError, self.phi4.marginalize, ["x4"]) self.assertRaises(ValueError, self.phi4.marginalize, ["x1", "x2", "x3", "x4"]) def test_normalize(self): def pdf2(x1, x2): return 2 * self.pdf2(x1, x2) phi2 = CustomDistribution(["x1", "x2"], pdf2) phi4 = phi2.copy() phi4.normalize() self.assertEqual(phi4.variables, phi2.variables) for inp in np.random.rand(1, 2): np_test.assert_almost_equal(phi4._pdf(inp[0], inp[1]), self.pdf2(inp[0], inp[1])) phi4 = phi2.normalize(inplace=False) self.assertEqual(phi4.variables, phi4.variables) for inp in np.random.rand(1, 2): np_test.assert_almost_equal(phi4._pdf(inp[0], inp[1]), self.pdf2(inp[0], inp[1])) def test_operate(self): phi1 = self.phi1.copy() phi1._operate(self.phi2, "product") self.assertEqual(phi1.variables, ["x", "y", "x1", "x2"]) for inp in np.random.rand(4, 4): self.assertEqual( phi1._pdf(*inp), self.phi1._pdf(inp[0], inp[1]) * self.phi2._pdf(inp[2], inp[3]), ) phi1 = self.phi1._operate(self.phi2, "product", inplace=False) self.assertEqual(phi1.variables, ["x", "y", "x1", "x2"]) for inp in np.random.rand(4, 4): self.assertEqual( phi1._pdf(*inp), self.phi1._pdf(inp[0], inp[1]) * self.phi2._pdf(inp[2], inp[3]), ) phi1 = self.phi1 * self.phi2 self.assertEqual(phi1.variables, ["x", "y", "x1", "x2"]) for inp in np.random.rand(4, 4): self.assertEqual( phi1._pdf(*inp), self.phi1._pdf(inp[0], inp[1]) * self.phi2._pdf(inp[2], inp[3]), ) phi3 = self.phi3.copy() phi3._operate(self.phi1, "product") self.assertEqual(phi3.variables, ["x", "y", "z"]) for inp in np.random.rand(4, 3): self.assertEqual( phi3._pdf(*inp), self.phi3._pdf(*inp) * self.phi1._pdf(inp[0], inp[1])) phi3 = self.phi3._operate(self.phi1, "product", inplace=False) self.assertEqual(phi3.variables, ["x", "y", "z"]) for inp in np.random.rand(4, 3): self.assertEqual( phi3._pdf(*inp), self.phi3._pdf(*inp) * self.phi1._pdf(inp[0], inp[1])) phi3 = self.phi3 * self.phi1 self.assertEqual(phi3.variables, ["x", "y", "z"]) for inp in np.random.rand(4, 3): self.assertEqual( phi3._pdf(*inp), self.phi3._pdf(*inp) * self.phi1._pdf(inp[0], inp[1])) phi3 = self.phi3.copy() phi3._operate(self.phi1, "divide") self.assertEqual(phi3.variables, ["x", "y", "z"]) for inp in np.random.rand(4, 3): self.assertEqual( phi3._pdf(*inp), self.phi3._pdf(*inp) / self.phi1._pdf(inp[0], inp[1])) phi3 = self.phi3._operate(self.phi1, "divide", inplace=False) self.assertEqual(phi3.variables, ["x", "y", "z"]) for inp in np.random.rand(4, 3): self.assertEqual( phi3._pdf(*inp), self.phi3._pdf(*inp) / self.phi1._pdf(inp[0], inp[1])) phi3 = self.phi3 / self.phi1 self.assertEqual(phi3.variables, ["x", "y", "z"]) for inp in np.random.rand(4, 3): self.assertEqual( phi3._pdf(*inp), self.phi3._pdf(*inp) / self.phi1._pdf(inp[0], inp[1])) phi4 = self.phi4.copy() phi4._operate(self.phi2, "product") self.assertEqual(phi4.variables, ["x1", "x2", "x3"]) for inp in np.random.rand(4, 3): self.assertEqual( phi4._pdf(*inp), self.phi4._pdf(*inp) * self.phi2._pdf(inp[0], inp[1])) phi4 = self.phi4._operate(self.phi2, "product", inplace=False) self.assertEqual(phi4.variables, ["x1", "x2", "x3"]) for inp in np.random.rand(4, 3): self.assertEqual( phi4._pdf(*inp), self.phi4._pdf(*inp) * self.phi2._pdf(inp[0], inp[1])) phi4 = self.phi4 * self.phi2 self.assertEqual(phi4.variables, ["x1", "x2", "x3"]) for inp in np.random.rand(4, 3): self.assertEqual( phi4._pdf(*inp), self.phi4._pdf(*inp) * self.phi2._pdf(inp[0], inp[1])) phi4 = self.phi4.copy() phi4._operate(self.phi2, "divide") self.assertEqual(phi4.variables, ["x1", "x2", "x3"]) for inp in np.random.rand(4, 3): self.assertEqual( phi4._pdf(*inp), self.phi4._pdf(*inp) / self.phi2._pdf(inp[0], inp[1])) phi4 = self.phi4._operate(self.phi2, "divide", inplace=False) self.assertEqual(phi4.variables, ["x1", "x2", "x3"]) for inp in np.random.rand(4, 3): self.assertEqual( phi4._pdf(*inp), self.phi4._pdf(*inp) / self.phi2._pdf(inp[0], inp[1])) phi4 = self.phi4 / self.phi2 self.assertEqual(phi4.variables, ["x1", "x2", "x3"]) for inp in np.random.rand(4, 3): self.assertEqual( phi4._pdf(*inp), self.phi4._pdf(*inp) / self.phi2._pdf(inp[0], inp[1])) def test_operate_error(self): self.assertRaises(TypeError, self.phi1._operate, 1, "product") self.assertRaises(TypeError, self.phi1._operate, 1, "divide") self.assertRaises(TypeError, self.phi1._operate, "1", "product") self.assertRaises(TypeError, self.phi1._operate, "1", "divide") self.assertRaises(TypeError, self.phi1._operate, self.phi2._pdf, "product") self.assertRaises(TypeError, self.phi1._operate, self.phi2._pdf, "divide") self.assertRaises(TypeError, self.phi1._operate, [1], "product") self.assertRaises(TypeError, self.phi1._operate, [1], "divide") self.assertRaises(TypeError, self.phi4._operate, 1, "product") self.assertRaises(TypeError, self.phi4._operate, 1, "divide") self.assertRaises(TypeError, self.phi4._operate, "1", "product") self.assertRaises(TypeError, self.phi4._operate, "1", "divide") self.assertRaises(TypeError, self.phi4._operate, self.phi2._pdf, "product") self.assertRaises(TypeError, self.phi4._operate, self.phi2._pdf, "divide") self.assertRaises(TypeError, self.phi4._operate, [1], "product") self.assertRaises(TypeError, self.phi4._operate, [1], "divide") self.assertRaises(TypeError, self.phi1._operate, 1, "product", False) self.assertRaises(TypeError, self.phi1._operate, 1, "divide", False) self.assertRaises(TypeError, self.phi1._operate, "1", "product", False) self.assertRaises(TypeError, self.phi1._operate, "1", "divide", False) self.assertRaises(TypeError, self.phi1._operate, self.phi2._pdf, "product", False) self.assertRaises(TypeError, self.phi1._operate, self.phi2._pdf, "divide", False) self.assertRaises(TypeError, self.phi1._operate, [1], "product", False) self.assertRaises(TypeError, self.phi1._operate, [1], "divide", False) self.assertRaises(TypeError, self.phi4._operate, 1, "product", False) self.assertRaises(TypeError, self.phi4._operate, 1, "divide", False) self.assertRaises(TypeError, self.phi4._operate, "1", "product", False) self.assertRaises(TypeError, self.phi4._operate, "1", "divide", False) self.assertRaises(TypeError, self.phi4._operate, self.phi2._pdf, "product", False) self.assertRaises(TypeError, self.phi4._operate, self.phi2._pdf, "divide", False) self.assertRaises(TypeError, self.phi4._operate, [1], "product", False) self.assertRaises(TypeError, self.phi4._operate, [1], "divide", False) self.assertRaises(ValueError, self.phi1.__truediv__, self.phi2) self.assertRaises(ValueError, self.phi1.__truediv__, self.phi3) self.assertRaises(ValueError, self.phi1.__truediv__, self.phi4) self.assertRaises(ValueError, self.phi2.__truediv__, self.phi3) self.assertRaises(ValueError, self.phi2.__truediv__, self.phi4) def test_copy(self): copy1 = self.phi1.copy() copy2 = self.phi3.copy() copy4 = copy1.copy() copy5 = copy2.copy() self.assertEqual(copy1.variables, copy4.variables) self.assertEqual(copy1._pdf, copy4._pdf) self.assertEqual(copy2.variables, copy5.variables) self.assertEqual(copy2._pdf, copy5._pdf) copy1.variables = ["A", "B"] self.assertEqual(copy4.variables, self.phi1.variables) def pdf(a, b): return (a + b) / (a * a + b * b) copy1._pdf = pdf copy1_pdf = pdf self.assertEqual(copy4._pdf, self.phi1._pdf) copy4.variables = ["X", "Y"] self.assertEqual(copy1.variables, ["A", "B"]) copy4._pdf = lambda a, b: a + b for inp in np.random.rand(4, 2): self.assertEqual(copy1._pdf(inp[0], inp[1]), copy1_pdf(inp[0], inp[1])) copy2.reduce([("x", 7.7)]) def reduced_pdf(y, z): return z * (np.power(7.7, 1) * np.power(y, 2)) / beta(7.7, y) self.assertEqual(copy5.variables, self.phi3.variables) self.assertEqual(copy5._pdf, self.phi3._pdf) copy5.reduce([("x", 11), ("z", 13)]) self.assertEqual(copy2.variables, ["y", "z"]) for inp in np.random.rand(4, 2): self.assertEqual(copy2._pdf(inp[0], inp[1]), reduced_pdf(inp[0], inp[1])) def tearDown(self): del self.phi1 del self.phi2 del self.phi3
class ContinuousFactor(BaseFactor): """ Base class for factors representing various multivariate representations. """ def __init__(self, variables, pdf, *args, **kwargs): """ Parameters ---------- variables: list or array-like The variables for wich the distribution is defined. pdf: function The probability density function of the distribution. Examples -------- >>> import numpy as np >>> from scipy.special import beta >>> from pgmpy.factors.continuous import ContinuousFactor # Two variable dirichlet distribution with alpha = (1,2) >>> def dirichlet_pdf(x, y): ... return (np.power(x, 1) * np.power(y, 2)) / beta(x, y) >>> dirichlet_factor = ContinuousFactor(['x', 'y'], dirichlet_pdf) >>> dirichlet_factor.scope() ['x', 'y'] >>> dirichlet_factor.assignment(5,6) 226800.0 """ if not isinstance(variables, (list, tuple, np.ndarray)): raise TypeError( "variables: Expected type list or array-like, " "got type {var_type}".format(var_type=type(variables))) if len(set(variables)) != len(variables): raise ValueError("Variable names cannot be same.") variables = list(variables) if isinstance(pdf, str): if pdf == 'gaussian': self.distribution = GaussianDistribution( variables=variables, mean=kwargs['mean'], covariance=kwargs['covariance']) else: raise NotImplementedError( "{dist} distribution not supported.", "Please use CustomDistribution".format(dist=pdf)) elif isinstance(pdf, CustomDistribution): self.distribution = pdf elif callable(pdf): self.distribution = CustomDistribution(variables=variables, distribution=pdf) else: raise ValueError( "pdf: Expected type: str or function, ", "Got: {instance}".format(instance=type(variables))) @property def pdf(self): """ Returns the pdf of the ContinuousFactor. """ return self.distribution.pdf @property def variable(self): return self.scope()[0] def scope(self): """ Returns the scope of the factor. Returns ------- list: List of variable names in the scope of the factor. Examples -------- >>> from pgmpy.factors.continuous import ContinuousFactor >>> from scipy.stats import multivariate_normal >>> normal_pdf = lambda x: multivariate_normal(x, [0, 0], [[1, 0], [0, 1]]) >>> phi = ContinuousFactor(['x1', 'x2'], normal_pdf) >>> phi.scope() ['x1', 'x2'] """ return self.distribution.variables def get_evidence(self): return self.scope()[1:] def assignment(self, *args): """ Returns a list of pdf assignments for the corresponding values. Parameters ---------- *args: values Values whose assignment is to be computed. Examples -------- >>> from pgmpy.factors.continuous import ContinuousFactor >>> from scipy.stats import multivariate_normal >>> normal_pdf = lambda x1, x2: multivariate_normal.pdf((x1, x2), [0, 0], [[1, 0], [0, 1]]) >>> phi = ContinuousFactor(['x1', 'x2'], normal_pdf) >>> phi.assignment(1, 2) 0.013064233284684921 """ return self.distribution.assignment(*args) def copy(self): """ Return a copy of the distribution. Returns ------- ContinuousFactor object: copy of the distribution Examples -------- >>> import numpy as np >>> from scipy.special import beta >>> from pgmpy.factors.continuous import ContinuousFactor # Two variable dirichlet distribution with alpha = (1,2) >>> def dirichlet_pdf(x, y): ... return (np.power(x, 1) * np.power(y, 2)) / beta(x, y) >>> dirichlet_factor = ContinuousFactor(['x', 'y'], dirichlet_pdf) >>> dirichlet_factor.variables ['x', 'y'] >>> copy_factor = dirichlet_factor.copy() >>> copy_factor.variables ['x', 'y'] """ return ContinuousFactor(self.scope(), self.distribution.copy()) def discretize(self, method, *args, **kwargs): """ Discretizes the continuous distribution into discrete probability masses using various methods. Parameters ---------- method : A Discretizer Class from pgmpy.discretize *args, **kwargs: The parameters to be given to the Discretizer Class. Returns ------- An n-D array or a DiscreteFactor object according to the discretiztion method used. Examples -------- >>> import numpy as np >>> from scipy.special import beta >>> from pgmpy.factors.continuous import ContinuousFactor >>> from pgmpy.factors.continuous import RoundingDiscretizer >>> def dirichlet_pdf(x, y): ... return (np.power(x, 1) * np.power(y, 2)) / beta(x, y) >>> dirichlet_factor = ContinuousFactor(['x', 'y'], dirichlet_pdf) >>> dirichlet_factor.discretize(RoundingDiscretizer, low=1, high=2, cardinality=5) # TODO: finish this """ return method(self, *args, **kwargs).get_discrete_values() def reduce(self, values, inplace=True): """ Reduces the factor to the context of the given variable values. Parameters ---------- values: list, array-like A list of tuples of the form (variable_name, variable_value). inplace: boolean If inplace=True it will modify the factor itself, else would return a new ContinuosFactor object. Returns ------- ContinuousFactor or None: if inplace=True (default) returns None if inplace=False returns a new ContinuousFactor instance. Examples -------- >>> import numpy as np >>> from scipy.special import beta >>> from pgmpy.factors.continuous import ContinuousFactor >>> def custom_pdf(x, y, z): ... return z*(np.power(x, 1) * np.power(y, 2)) / beta(x, y) >>> custom_factor = ContinuousFactor(['x', 'y', 'z'], custom_pdf) >>> custom_factor.variables ['x', 'y', 'z'] >>> custom_factor.assignment(1, 2, 3) 24.0 >>> custom_factor.reduce([('y', 2)]) >>> custom_factor.variables ['x', 'z'] >>> custom_factor.assignment(1, 3) 24.0 """ phi = self if inplace else self.copy() phi.distribution = phi.distribution.reduce(values, inplace=False) if not inplace: return phi def marginalize(self, variables, inplace=True): """ Marginalize the factor with respect to the given variables. Parameters ---------- variables: list, array-like List of variables with respect to which factor is to be maximized. inplace: boolean If inplace=True it will modify the factor itself, else would return a new ContinuousFactor instance. Returns ------- DiscreteFactor or None: if inplace=True (default) returns None if inplace=False returns a new ContinuousFactor instance. Examples -------- >>> from pgmpy.factors.continuous import ContinuousFactor >>> from scipy.stats import multivariate_normal >>> std_normal_pdf = lambda *x: multivariate_normal.pdf(x, [0, 0], [[1, 0], [0, 1]]) >>> std_normal = ContinuousFactor(['x1', 'x2'], std_normal_pdf) >>> std_normal.scope() ['x1', 'x2'] >>> std_normal.assignment([1, 1]) 0.058549831524319168 >>> std_normal.marginalize(['x2']) >>> std_normal.scope() ['x1'] >>> std_normal.assignment(1) """ phi = self if inplace else self.copy() phi.distribution = phi.distribution.marginalize(variables, inplace=False) if not inplace: return phi def normalize(self, inplace=True): """ Normalizes the pdf of the continuous factor so that it integrates to 1 over all the variables. Parameters ---------- inplace: boolean If inplace=True it will modify the factor itself, else would return a new factor. Returns ------- ContinuousFactor or None: if inplace=True (default) returns None if inplace=False returns a new ContinuousFactor instance. Examples -------- >>> from pgmpy.factors.continuous import ContinuousFactor >>> from scipy.stats import multivariate_normal >>> std_normal_pdf = lambda x: 2 * multivariate_normal.pdf(x, [0, 0], [[1, 0], [0, 1]]) >>> std_normal = ContinuousFactor(['x1', 'x2'], std_normal_pdf) >>> std_normal.assignment(1, 1) 0.117099663049 >>> std_normal.normalize() >>> std_normal.assignment(1, 1) 0.0585498315243 """ phi = self if inplace else self.copy() phi.distriution = phi.distribution.normalize(inplace=False) if not inplace: return phi def is_valid_cpd(self): return self.distribution.is_valid_cpd() def _operate(self, other, operation, inplace=True): """ Gives the ContinuousFactor operation (product or divide) with the other factor. Parameters ---------- other: ContinuousFactor The ContinuousFactor to be multiplied. operation: String 'product' for multiplication operation and 'divide' for division operation. inplace: boolean If inplace=True it will modify the factor itself, else would return a new factor. Returns ------- ContinuousFactor or None: if inplace=True (default) returns None if inplace=False returns a new `DiscreteFactor` instance. """ if not isinstance(other, ContinuousFactor): raise TypeError( "ContinuousFactor objects can only be multiplied ", "or divided with another ContinuousFactor object. ", "Got {other_type}, expected: ContinuousFactor.".format( other_type=type(other))) phi = self if inplace else self.copy() phi.distribution = phi.distribution._operate(other=other.distribution, operation=operation, inplace=False) if not inplace: return phi def product(self, other, inplace=True): """ Gives the ContinuousFactor product with the other factor. Parameters ---------- other: ContinuousFactor The ContinuousFactor to be multiplied. Returns ------- ContinuousFactor or None: if inplace=True (default) returns None if inplace=False returns a new `ContinuousFactor` instance. Example ------- >>> from pgmpy.factors.continuous import ContinuousFactor >>> from scipy.stats import multivariate_normal >>> sn_pdf1 = lambda x: multivariate_normal.pdf([x], [0], [[1]]) >>> sn_pdf2 = lambda x1,x2: multivariate_normal.pdf([x1, x2], [0, 0], [[1, 0], [0, 1]]) >>> sn1 = ContinuousFactor(['x2'], sn_pdf1) >>> sn2 = ContinuousFactor(['x1', 'x2'], sn_pdf2) >>> sn3 = sn1.product(sn2, inplace=False) >>> sn3.assignment(0, 0) 0.063493635934240983 >>> sn3 = sn1 * sn2 >>> sn3.assignment(0, 0) 0.063493635934240983 """ return self._operate(other, 'product', inplace) def divide(self, other, inplace=True): """ Gives the ContinuousFactor divide with the other factor. Parameters ---------- other: ContinuousFactor The ContinuousFactor to be multiplied. Returns ------- ContinuousFactor or None: if inplace=True (default) returns None if inplace=False returns a new `DiscreteFactor` instance. Example ------- >>> from pgmpy.factors.continuous import ContinuousFactor >>> from scipy.stats import multivariate_normal >>> sn_pdf1 = lambda x: multivariate_normal.pdf([x], [0], [[1]]) >>> sn_pdf2 = lambda x1,x2: multivariate_normal.pdf([x1, x2], [0, 0], [[1, 0], [0, 1]]) >>> sn1 = ContinuousFactor(['x2'], sn_pdf1) >>> sn2 = ContinuousFactor(['x1', 'x2'], sn_pdf2) >>> sn4 = sn2.divide(sn1, inplace=False) >>> sn4.assignment(0, 0) 0.3989422804014327 >>> sn4 = sn2 / sn1 >>> sn4.assignment(0, 0) 0.3989422804014327 """ if set(other.scope()) - set(self.scope()): raise ValueError("Scope of divisor should be a subset of dividend") return self._operate(other, 'divide', inplace) def __mul__(self, other): return self.product(other, inplace=False) def __rmul__(self, other): return self.__mul__(other) def __truediv__(self, other): return self.divide(other, inplace=False) __div__ = __truediv__
class ContinuousFactor(BaseFactor): """ Base class for factors representing various multivariate representations. """ def __init__(self, variables, pdf, *args, **kwargs): """ Parameters ---------- variables: list or array-like The variables for wich the distribution is defined. pdf: function The probability density function of the distribution. Examples -------- >>> import numpy as np >>> from scipy.special import beta >>> from pgmpy.factors.continuous import ContinuousFactor # Two variable drichlet ditribution with alpha = (1,2) >>> def drichlet_pdf(x, y): ... return (np.power(x, 1) * np.power(y, 2)) / beta(x, y) >>> dirichlet_factor = ContinuousFactor(['x', 'y'], drichlet_pdf) >>> dirichlet_factor.scope() ['x', 'y'] >>> dirichlet_factor.assignment(5,6) 226800.0 """ if not isinstance(variables, (list, tuple, np.ndarray)): raise TypeError("variables: Expected type list or array-like, " "got type {var_type}".format(var_type=type(variables))) if len(set(variables)) != len(variables): raise ValueError("Variable names cannot be same.") variables = list(variables) if isinstance(pdf, str): if pdf == 'gaussian': self.distribution = GaussianDistribution( variables=variables, mean=kwargs['mean'], covariance=kwargs['covariance']) else: raise NotImplementedError("{dist} distribution not supported.", "Please use CustomDistribution". format(dist=pdf)) elif isinstance(pdf, CustomDistribution): self.distribution = pdf elif callable(pdf): self.distribution = CustomDistribution( variables=variables, distribution=pdf) else: raise ValueError("pdf: Expected type: str or function, ", "Got: {instance}".format(instance=type(variables))) @property def pdf(self): """ Returns the pdf of the ContinuousFactor. """ return self.distribution.pdf def scope(self): """ Returns the scope of the factor. Returns ------- list: List of variable names in the scope of the factor. Examples -------- >>> from pgmpy.factors.continuous import ContinuousFactor >>> from scipy.stats import multivariate_normal >>> normal_pdf = lambda x: multivariate_normal(x, [0, 0], [[1, 0], [0, 1]]) >>> phi = ContinuousFactor(['x1', 'x2'], normal_pdf) >>> phi.scope() ['x1', 'x2'] """ return self.distribution.variables def assignment(self, *args): """ Returns a list of pdf assignments for the corresponding values. Parameters ---------- *args: values Values whose assignment is to be computed. Examples -------- >>> from pgmpy.factors.continuous import ContinuousFactor >>> from scipy.stats import multivariate_normal >>> normal_pdf = lambda x1, x2: multivariate_normal.pdf((x1, x2), [0, 0], [[1, 0], [0, 1]]) >>> phi = ContinuousFactor(['x1', 'x2'], normal_pdf) >>> phi.assignment(1, 2) 0.013064233284684921 """ return self.distribution.assignment(*args) def copy(self): """ Return a copy of the distribution. Returns ------- ContinuousFactor object: copy of the distribution Examples -------- >>> import numpy as np >>> from scipy.special import beta >>> from pgmpy.factors.continuous import ContinuousFactor # Two variable drichlet ditribution with alpha = (1,2) >>> def dirichlet_pdf(x, y): ... return (np.power(x, 1) * np.power(y, 2)) / beta(x, y) >>> dirichlet_factor = ContinuousFactor(['x', 'y'], dirichlet_pdf) >>> dirichlet_factor.variables ['x', 'y'] >>> copy_factor = dirichlet_factor.copy() >>> copy_factor.variables ['x', 'y'] """ return ContinuousFactor(self.scope(), self.distribution.copy()) def discretize(self, method, *args, **kwargs): """ Discretizes the continuous distribution into discrete probability masses using various methods. Parameters ---------- method : A Discretizer Class from pgmpy.discretize *args, **kwargs: The parameters to be given to the Discretizer Class. Returns ------- An n-D array or a DiscreteFactor object according to the discretiztion method used. Examples -------- >>> import numpy as np >>> from scipy.special import beta >>> from pgmpy.factors.continuous import ContinuousFactor >>> from pgmpy.factors.continuous import RoundingDiscretizer >>> def dirichlet_pdf(x, y): ... return (np.power(x, 1) * np.power(y, 2)) / beta(x, y) >>> dirichlet_factor = ContinuousFactor(['x', 'y'], dirichlet_pdf) >>> dirichlet_factor.discretize(RoundingDiscretizer, low=1, high=2, cardinality=5) # TODO: finish this """ return method(self, *args, **kwargs).get_discrete_values() def reduce(self, values, inplace=True): """ Reduces the factor to the context of the given variable values. Parameters ---------- values: list, array-like A list of tuples of the form (variable_name, variable_value). inplace: boolean If inplace=True it will modify the factor itself, else would return a new ContinuosFactor object. Returns ------- ContinuousFactor or None: if inplace=True (default) returns None if inplace=False returns a new ContinuousFactor instance. Examples -------- >>> import numpy as np >>> from scipy.special import beta >>> from pgmpy.factors.continuous import ContinuousFactor >>> def custom_pdf(x, y, z): ... return z*(np.power(x, 1) * np.power(y, 2)) / beta(x, y) >>> custom_factor = ContinuousFactor(['x', 'y', 'z'], custom_pdf) >>> custom_factor.variables ['x', 'y', 'z'] >>> custom_factor.assignment(1, 2, 3) 24.0 >>> custom_factor.reduce([('y', 2)]) >>> custom_factor.variables ['x', 'z'] >>> custom_factor.assignment(1, 3) 24.0 """ phi = self if inplace else self.copy() phi.distribution = phi.distribution.reduce(values, inplace=False) if not inplace: return phi def marginalize(self, variables, inplace=True): """ Marginalize the factor with respect to the given variables. Parameters ---------- variables: list, array-like List of variables with respect to which factor is to be maximized. inplace: boolean If inplace=True it will modify the factor itself, else would return a new ContinuousFactor instance. Returns ------- DiscreteFactor or None: if inplace=True (default) returns None if inplace=False returns a new ContinuousFactor instance. Examples -------- >>> from pgmpy.factors.continuous import ContinuousFactor >>> from scipy.stats import multivariate_normal >>> std_normal_pdf = lambda *x: multivariate_normal.pdf(x, [0, 0], [[1, 0], [0, 1]]) >>> std_normal = ContinuousFactor(['x1', 'x2'], std_normal_pdf) >>> std_normal.scope() ['x1', 'x2'] >>> std_normal.assignment([1, 1]) 0.058549831524319168 >>> std_normal.marginalize(['x2']) >>> std_normal.scope() ['x1'] >>> std_normal.assignment(1) """ phi = self if inplace else self.copy() phi.distribution = phi.distribution.marginalize(variables, inplace=False) if not inplace: return phi def normalize(self, inplace=True): """ Normalizes the pdf of the continuous factor so that it integrates to 1 over all the variables. Parameters ---------- inplace: boolean If inplace=True it will modify the factor itself, else would return a new factor. Returns ------- ContinuousFactor or None: if inplace=True (default) returns None if inplace=False returns a new ContinuousFactor instance. Examples -------- >>> from pgmpy.factors.continuous import ContinuousFactor >>> from scipy.stats import multivariate_normal >>> std_normal_pdf = lambda x: 2 * multivariate_normal.pdf(x, [0, 0], [[1, 0], [0, 1]]) >>> std_normal = ContinuousFactor(['x1', 'x2'], std_normal_pdf) >>> std_normal.assignment(1, 1) 0.117099663049 >>> std_normal.normalize() >>> std_normal.assignment(1, 1) 0.0585498315243 """ phi = self if inplace else self.copy() phi.distriution = phi.distribution.normalize(inplace=False) if not inplace: return phi def _operate(self, other, operation, inplace=True): """ Gives the ContinuousFactor operation (product or divide) with the other factor. Parameters ---------- other: ContinuousFactor The ContinuousFactor to be multiplied. operation: String 'product' for multiplication operation and 'divide' for division operation. inplace: boolean If inplace=True it will modify the factor itself, else would return a new factor. Returns ------- ContinuousFactor or None: if inplace=True (default) returns None if inplace=False returns a new `DiscreteFactor` instance. """ if not isinstance(other, ContinuousFactor): raise TypeError("ContinuousFactor objects can only be multiplied ", "or divided with another ContinuousFactor object. ", "Got {other_type}, expected: ContinuousFactor.".format( other_type=type(other))) phi = self if inplace else self.copy() phi.distribution = phi.distribution._operate( other=other.distribution, operation=operation, inplace=False) if not inplace: return phi def product(self, other, inplace=True): """ Gives the ContinuousFactor product with the other factor. Parameters ---------- other: ContinuousFactor The ContinuousFactor to be multiplied. Returns ------- ContinuousFactor or None: if inplace=True (default) returns None if inplace=False returns a new `ContinuousFactor` instance. Example ------- >>> from pgmpy.factors.continuous import ContinuousFactor >>> from scipy.stats import multivariate_normal >>> sn_pdf1 = lambda x: multivariate_normal.pdf([x], [0], [[1]]) >>> sn_pdf2 = lambda x1,x2: multivariate_normal.pdf([x1, x2], [0, 0], [[1, 0], [0, 1]]) >>> sn1 = ContinuousFactor(['x2'], sn_pdf1) >>> sn2 = ContinuousFactor(['x1', 'x2'], sn_pdf2) >>> sn3 = sn1.product(sn2, inplace=False) >>> sn3.assignment(0, 0) 0.063493635934240983 >>> sn3 = sn1 * sn2 >>> sn3.assignment(0, 0) 0.063493635934240983 """ return self._operate(other, 'product', inplace) def divide(self, other, inplace=True): """ Gives the ContinuousFactor divide with the other factor. Parameters ---------- other: ContinuousFactor The ContinuousFactor to be multiplied. Returns ------- ContinuousFactor or None: if inplace=True (default) returns None if inplace=False returns a new `DiscreteFactor` instance. Example ------- >>> from pgmpy.factors.continuous import ContinuousFactor >>> from scipy.stats import multivariate_normal >>> sn_pdf1 = lambda x: multivariate_normal.pdf([x], [0], [[1]]) >>> sn_pdf2 = lambda x1,x2: multivariate_normal.pdf([x1, x2], [0, 0], [[1, 0], [0, 1]]) >>> sn1 = ContinuousFactor(['x2'], sn_pdf1) >>> sn2 = ContinuousFactor(['x1', 'x2'], sn_pdf2) >>> sn4 = sn2.divide(sn1, inplace=False) >>> sn4.assignment(0, 0) 0.3989422804014327 >>> sn4 = sn2 / sn1 >>> sn4.assignment(0, 0) 0.3989422804014327 """ if set(other.scope()) - set(self.scope()): raise ValueError("Scope of divisor should be a subset of dividend") return self._operate(other, 'divide', inplace) def __mul__(self, other): return self.product(other, inplace=False) def __rmul__(self, other): return self.__mul__(other) def __truediv__(self, other): return self.divide(other, inplace=False) __div__ = __truediv__
def setUp(self): self.phi1 = CustomDistribution(['x', 'y'], self.pdf1) self.phi2 = CustomDistribution(['x1', 'x2'], self.pdf2) self.phi3 = CustomDistribution(['x', 'y', 'z'], self.pdf3) self.phi4 = CustomDistribution(['x1', 'x2', 'x3'], self.pdf4)
class TestCustomDistributionMethods(unittest.TestCase): def pdf1(self, x, y): return np.power(x, 1) * np.power(y, 2) / beta(x, y) def pdf2(self, x1, x2): return multivariate_normal.pdf([x1, x2], [0, 0], [[1, 0], [0, 1]]) def pdf3(self, x, y, z): return z * (np.power(x, 1) * np.power(y, 2)) / beta(x, y) def pdf4(self, x1, x2, x3): return multivariate_normal.pdf([x1, x2, x3], [0, 0, 0], [[1, 0, 0], [0, 1, 0], [0, 0, 1]]) def setUp(self): self.phi1 = CustomDistribution(['x', 'y'], self.pdf1) self.phi2 = CustomDistribution(['x1', 'x2'], self.pdf2) self.phi3 = CustomDistribution(['x', 'y', 'z'], self.pdf3) self.phi4 = CustomDistribution(['x1', 'x2', 'x3'], self.pdf4) def test_variables(self): self.assertEqual(self.phi1.variables, self.phi1._variables) self.assertEqual(self.phi2.variables, self.phi2._variables) self.assertEqual(self.phi3.variables, self.phi3._variables) def test_assignment(self): self.assertEqual(self.phi1.assignment(1.212, 2), self.pdf1(1.212, 2)) self.assertEqual(self.phi2.assignment(1, -2.231), self.pdf2(1, -2.231)) self.assertEqual(self.phi3.assignment(1.212, 2.213, -3), self.pdf3(1.212, 2.213, -3)) def test_reduce(self): phi1 = self.phi1.copy() phi1.reduce([('x', 1)]) reduced_pdf1 = lambda y: (np.power(1, 1) * np.power(y, 2))/beta(1, y) self.assertEqual(phi1.variables, ['y']) for inp in np.random.rand(4): self.assertEqual(phi1._pdf(inp), reduced_pdf1(inp)) self.assertEqual(phi1._pdf(y=inp), reduced_pdf1(inp)) phi1 = self.phi1.reduce([('x', 1)], inplace=False) self.assertEqual(phi1.variables, ['y']) for inp in np.random.rand(4): self.assertEqual(phi1._pdf(inp), reduced_pdf1(inp)) self.assertEqual(phi1._pdf(y=inp), reduced_pdf1(inp)) phi2 = self.phi2.copy() phi2.reduce([('x2', 7.213)]) reduced_pdf2 = lambda x1: multivariate_normal.pdf([x1, 7.213], [0, 0], [[1, 0], [0, 1]]) self.assertEqual(phi2.variables, ['x1']) for inp in np.random.rand(4): self.assertEqual(phi2._pdf(inp), reduced_pdf2(inp)) self.assertEqual(phi2._pdf(x1=inp), reduced_pdf2(inp)) phi2 = self.phi2.reduce([('x2', 7.213)], inplace=False) self.assertEqual(phi2.variables, ['x1']) for inp in np.random.rand(4): self.assertEqual(phi2._pdf(inp), reduced_pdf2(inp)) self.assertEqual(phi2._pdf(x1=inp), reduced_pdf2(inp)) phi3 = self.phi3.copy() phi3.reduce([('y', 0.112), ('z', 23)]) reduced_pdf4 = lambda x: 23*(np.power(x, 1)*np.power(0.112, 2))/beta(x, 0.112) self.assertEqual(phi3.variables, ['x']) for inp in np.random.rand(4): self.assertEqual(phi3._pdf(inp), reduced_pdf4(inp)) self.assertEqual(phi3._pdf(x=inp), reduced_pdf4(inp)) phi3 = self.phi3.copy() phi3.reduce([('y', 0.112)]) reduced_pdf3 = lambda x, z: z*(np.power(x, 1)*np.power(0.112, 2))/beta(x, 0.112) self.assertEqual(phi3.variables, ['x', 'z']) for inp in np.random.rand(4, 2): self.assertEqual(phi3._pdf(inp[0], inp[1]), reduced_pdf3(inp[0], inp[1])) self.assertEqual(phi3._pdf(x=inp[0], z=inp[1]), reduced_pdf3(inp[0], inp[1])) phi3 = self.phi3.reduce([('y', 0.112)], inplace=False) self.assertEqual(phi3.variables, ['x', 'z']) for inp in np.random.rand(4, 2): self.assertEqual(phi3._pdf(inp[0], inp[1]), reduced_pdf3(inp[0], inp[1])) self.assertEqual(phi3._pdf(x=inp[0], z=inp[1]), reduced_pdf3(inp[0], inp[1])) self.assertEqual(phi3._pdf(inp[0], z=inp[1]), reduced_pdf3(inp[0], inp[1])) phi3 = self.phi3.reduce([('y', 0.112), ('z', 23)], inplace=False) self.assertEqual(phi3.variables, ['x']) for inp in np.random.rand(4): self.assertEqual(phi3._pdf(inp), reduced_pdf4(inp)) self.assertEqual(phi3._pdf(x=inp), reduced_pdf4(inp)) def test_reduce_error(self): self.assertRaises(TypeError, self.phi1.reduce, 'x1') self.assertRaises(TypeError, self.phi1.reduce, set(['x', 'y'])) self.assertRaises(TypeError, self.phi1.reduce, {'x': 1, 'y': 1}) self.assertRaises(TypeError, self.phi4.reduce, 'x4') self.assertRaises(TypeError, self.phi4.reduce, set(['x1', 'x2', 'x3'])) self.assertRaises(TypeError, self.phi4.reduce, {'x1': 1, 'x2': 1, 'x3': 1}) self.assertRaises(ValueError, self.phi1.reduce, [('z', 3)]) self.assertRaises(ValueError, self.phi1.reduce, [('x', 0), ('y', 1), ('z', 4)]) self.assertRaises(ValueError, self.phi4.reduce, [('x4', 7)]) self.assertRaises(ValueError, self.phi4.reduce, [('x1', 1), ('x2', 2), ('x3', 3), ('x4', 4)]) def test_marginalize(self): phi2 = self.phi2.copy() phi2.marginalize(['x2']) self.assertEqual(phi2.variables, ['x1']) for inp in np.random.rand(4): np_test.assert_almost_equal(phi2._pdf(inp), multivariate_normal.pdf([inp], [0], [[1]])) phi2 = self.phi2.marginalize(['x2'], inplace=False) self.assertEqual(phi2.variables, ['x1']) for inp in np.random.rand(4): np_test.assert_almost_equal(phi2._pdf(inp), multivariate_normal.pdf([inp], [0], [[1]])) phi4 = self.phi4.copy() phi4.marginalize(['x2']) self.assertEqual(phi4.variables, ['x1', 'x3']) for inp in np.random.rand(4, 2): np_test.assert_almost_equal( phi4._pdf(inp[0], inp[1]), multivariate_normal.pdf( [inp[0], inp[1]], [0, 0], [[1, 0], [0, 1]])) phi4.marginalize(['x3']) self.assertEqual(phi4.variables, ['x1']) for inp in np.random.rand(1): np_test.assert_almost_equal(phi4._pdf(inp), multivariate_normal.pdf([inp], [0], [[1]])) phi4 = self.phi4.marginalize(['x2'], inplace=False) self.assertEqual(phi4.variables, ['x1', 'x3']) for inp in np.random.rand(4, 2): np_test.assert_almost_equal(phi4._pdf(inp[0], inp[1]), multivariate_normal.pdf([inp[0], inp[1]], [0, 0], [[1, 0], [0, 1]])) phi4 = phi4.marginalize(['x3'], inplace=False) self.assertEqual(phi4.variables, ['x1']) for inp in np.random.rand(1): np_test.assert_almost_equal(phi4._pdf(inp), multivariate_normal.pdf([inp], [0], [[1]])) def test_marginalize_error(self): self.assertRaises(TypeError, self.phi1.marginalize, 'x1') self.assertRaises(TypeError, self.phi1.marginalize, set(['x', 'y'])) self.assertRaises(TypeError, self.phi1.marginalize, {'x': 1, 'y': 1}) self.assertRaises(TypeError, self.phi4.marginalize, 'x4') self.assertRaises(TypeError, self.phi4.marginalize, set(['x1', 'x2', 'x3'])) self.assertRaises(TypeError, self.phi4.marginalize, {'x1': 1, 'x2': 1, 'x3': 1}) self.assertRaises(ValueError, self.phi1.marginalize, ['z']) self.assertRaises(ValueError, self.phi1.marginalize, ['x', 'y', 'z']) self.assertRaises(ValueError, self.phi4.marginalize, ['x4']) self.assertRaises(ValueError, self.phi4.marginalize, ['x1', 'x2', 'x3', 'x4']) def test_normalize(self): def pdf2(x1, x2): return 2 * self.pdf2(x1, x2) phi2 = CustomDistribution(['x1', 'x2'], pdf2) phi4 = phi2.copy() phi4.normalize() self.assertEqual(phi4.variables, phi2.variables) for inp in np.random.rand(1, 2): np_test.assert_almost_equal(phi4._pdf(inp[0], inp[1]), self.pdf2(inp[0], inp[1])) phi4 = phi2.normalize(inplace=False) self.assertEqual(phi4.variables, phi4.variables) for inp in np.random.rand(1, 2): np_test.assert_almost_equal(phi4._pdf(inp[0], inp[1]), self.pdf2(inp[0], inp[1])) def test_operate(self): phi1 = self.phi1.copy() phi1._operate(self.phi2, 'product') self.assertEqual(phi1.variables, ['x', 'y', 'x1', 'x2']) for inp in np.random.rand(4, 4): self.assertEqual(phi1._pdf(*inp), self.phi1._pdf(inp[0], inp[1]) * self.phi2._pdf(inp[2], inp[3])) phi1 = self.phi1._operate(self.phi2, 'product', inplace=False) self.assertEqual(phi1.variables, ['x', 'y', 'x1', 'x2']) for inp in np.random.rand(4, 4): self.assertEqual(phi1._pdf(*inp), self.phi1._pdf(inp[0], inp[1]) * self.phi2._pdf(inp[2], inp[3])) phi1 = self.phi1 * self.phi2 self.assertEqual(phi1.variables, ['x', 'y', 'x1', 'x2']) for inp in np.random.rand(4, 4): self.assertEqual(phi1._pdf(*inp), self.phi1._pdf(inp[0], inp[1]) * self.phi2._pdf(inp[2], inp[3])) phi3 = self.phi3.copy() phi3._operate(self.phi1, 'product') self.assertEqual(phi3.variables, ['x', 'y', 'z']) for inp in np.random.rand(4, 3): self.assertEqual(phi3._pdf(*inp), self.phi3._pdf(*inp) * self.phi1._pdf(inp[0], inp[1])) phi3 = self.phi3._operate(self.phi1, 'product', inplace=False) self.assertEqual(phi3.variables, ['x', 'y', 'z']) for inp in np.random.rand(4, 3): self.assertEqual(phi3._pdf(*inp), self.phi3._pdf(*inp) * self.phi1._pdf(inp[0], inp[1])) phi3 = self.phi3 * self.phi1 self.assertEqual(phi3.variables, ['x', 'y', 'z']) for inp in np.random.rand(4, 3): self.assertEqual(phi3._pdf(*inp), self.phi3._pdf(*inp) * self.phi1._pdf(inp[0], inp[1])) phi3 = self.phi3.copy() phi3._operate(self.phi1, 'divide') self.assertEqual(phi3.variables, ['x', 'y', 'z']) for inp in np.random.rand(4, 3): self.assertEqual(phi3._pdf(*inp), self.phi3._pdf(*inp) / self.phi1._pdf(inp[0], inp[1])) phi3 = self.phi3._operate(self.phi1, 'divide', inplace=False) self.assertEqual(phi3.variables, ['x', 'y', 'z']) for inp in np.random.rand(4, 3): self.assertEqual(phi3._pdf(*inp), self.phi3._pdf(*inp) / self.phi1._pdf(inp[0], inp[1])) phi3 = self.phi3 / self.phi1 self.assertEqual(phi3.variables, ['x', 'y', 'z']) for inp in np.random.rand(4, 3): self.assertEqual(phi3._pdf(*inp), self.phi3._pdf(*inp) / self.phi1._pdf(inp[0], inp[1])) phi4 = self.phi4.copy() phi4._operate(self.phi2, 'product') self.assertEqual(phi4.variables, ['x1', 'x2', 'x3']) for inp in np.random.rand(4, 3): self.assertEqual(phi4._pdf(*inp), self.phi4._pdf(*inp) * self.phi2._pdf(inp[0], inp[1])) phi4 = self.phi4._operate(self.phi2, 'product', inplace=False) self.assertEqual(phi4.variables, ['x1', 'x2', 'x3']) for inp in np.random.rand(4, 3): self.assertEqual(phi4._pdf(*inp), self.phi4._pdf(*inp) * self.phi2._pdf(inp[0], inp[1])) phi4 = self.phi4 * self.phi2 self.assertEqual(phi4.variables, ['x1', 'x2', 'x3']) for inp in np.random.rand(4, 3): self.assertEqual(phi4._pdf(*inp), self.phi4._pdf(*inp) * self.phi2._pdf(inp[0], inp[1])) phi4 = self.phi4.copy() phi4._operate(self.phi2, 'divide') self.assertEqual(phi4.variables, ['x1', 'x2', 'x3']) for inp in np.random.rand(4, 3): self.assertEqual(phi4._pdf(*inp), self.phi4._pdf(*inp) / self.phi2._pdf(inp[0], inp[1])) phi4 = self.phi4._operate(self.phi2, 'divide', inplace=False) self.assertEqual(phi4.variables, ['x1', 'x2', 'x3']) for inp in np.random.rand(4, 3): self.assertEqual(phi4._pdf(*inp), self.phi4._pdf(*inp) / self.phi2._pdf(inp[0], inp[1])) phi4 = self.phi4 / self.phi2 self.assertEqual(phi4.variables, ['x1', 'x2', 'x3']) for inp in np.random.rand(4, 3): self.assertEqual(phi4._pdf(*inp), self.phi4._pdf(*inp) / self.phi2._pdf(inp[0], inp[1])) def test_operate_error(self): self.assertRaises(TypeError, self.phi1._operate, 1, 'product') self.assertRaises(TypeError, self.phi1._operate, 1, 'divide') self.assertRaises(TypeError, self.phi1._operate, '1', 'product') self.assertRaises(TypeError, self.phi1._operate, '1', 'divide') self.assertRaises(TypeError, self.phi1._operate, self.phi2._pdf, 'product') self.assertRaises(TypeError, self.phi1._operate, self.phi2._pdf, 'divide') self.assertRaises(TypeError, self.phi1._operate, [1], 'product') self.assertRaises(TypeError, self.phi1._operate, [1], 'divide') self.assertRaises(TypeError, self.phi4._operate, 1, 'product') self.assertRaises(TypeError, self.phi4._operate, 1, 'divide') self.assertRaises(TypeError, self.phi4._operate, '1', 'product') self.assertRaises(TypeError, self.phi4._operate, '1', 'divide') self.assertRaises(TypeError, self.phi4._operate, self.phi2._pdf, 'product') self.assertRaises(TypeError, self.phi4._operate, self.phi2._pdf, 'divide') self.assertRaises(TypeError, self.phi4._operate, [1], 'product') self.assertRaises(TypeError, self.phi4._operate, [1], 'divide') self.assertRaises(TypeError, self.phi1._operate, 1, 'product', False) self.assertRaises(TypeError, self.phi1._operate, 1, 'divide', False) self.assertRaises(TypeError, self.phi1._operate, '1', 'product', False) self.assertRaises(TypeError, self.phi1._operate, '1', 'divide', False) self.assertRaises(TypeError, self.phi1._operate, self.phi2._pdf, 'product', False) self.assertRaises(TypeError, self.phi1._operate, self.phi2._pdf, 'divide', False) self.assertRaises(TypeError, self.phi1._operate, [1], 'product', False) self.assertRaises(TypeError, self.phi1._operate, [1], 'divide', False) self.assertRaises(TypeError, self.phi4._operate, 1, 'product', False) self.assertRaises(TypeError, self.phi4._operate, 1, 'divide', False) self.assertRaises(TypeError, self.phi4._operate, '1', 'product', False) self.assertRaises(TypeError, self.phi4._operate, '1', 'divide', False) self.assertRaises(TypeError, self.phi4._operate, self.phi2._pdf, 'product', False) self.assertRaises(TypeError, self.phi4._operate, self.phi2._pdf, 'divide', False) self.assertRaises(TypeError, self.phi4._operate, [1], 'product', False) self.assertRaises(TypeError, self.phi4._operate, [1], 'divide', False) self.assertRaises(ValueError, self.phi1.__truediv__, self.phi2) self.assertRaises(ValueError, self.phi1.__truediv__, self.phi3) self.assertRaises(ValueError, self.phi1.__truediv__, self.phi4) self.assertRaises(ValueError, self.phi2.__truediv__, self.phi3) self.assertRaises(ValueError, self.phi2.__truediv__, self.phi4) def test_copy(self): copy1 = self.phi1.copy() copy2 = self.phi3.copy() copy4 = copy1.copy() copy5 = copy2.copy() self.assertEqual(copy1.variables, copy4.variables) self.assertEqual(copy1._pdf, copy4._pdf) self.assertEqual(copy2.variables, copy5.variables) self.assertEqual(copy2._pdf, copy5._pdf) copy1.variables = ['A', 'B'] self.assertEqual(copy4.variables, self.phi1.variables) def pdf(a, b): return (a + b) / (a * a + b * b) copy1._pdf = pdf copy1_pdf = pdf self.assertEqual(copy4._pdf, self.phi1._pdf) copy4.variables = ['X', 'Y'] self.assertEqual(copy1.variables, ['A', 'B']) copy4._pdf = lambda a, b: a + b for inp in np.random.rand(4, 2): self.assertEqual(copy1._pdf(inp[0], inp[1]), copy1_pdf(inp[0], inp[1])) copy2.reduce([('x', 7.7)]) def reduced_pdf(y, z): return z*(np.power(7.7, 1) * np.power(y, 2)) / beta(7.7, y) self.assertEqual(copy5.variables, self.phi3.variables) self.assertEqual(copy5._pdf, self.phi3._pdf) copy5.reduce([('x', 11), ('z', 13)]) self.assertEqual(copy2.variables, ['y', 'z']) for inp in np.random.rand(4, 2): self.assertEqual(copy2._pdf(inp[0], inp[1]), reduced_pdf(inp[0], inp[1])) def tearDown(self): del self.phi1 del self.phi2 del self.phi3