|  |  |  | @ -200,6 +200,228 @@ TEST(GaussianMixtureFactor, Error) { | 
		
	
		
			
				|  |  |  |  |       4.0, mixtureFactor.error({continuousValues, discreteValues}), 1e-9); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | /* ************************************************************************* */ | 
		
	
		
			
				|  |  |  |  | /**
 | 
		
	
		
			
				|  |  |  |  |  * Test a simple Gaussian Mixture Model represented as P(m)P(z|m) | 
		
	
		
			
				|  |  |  |  |  * where m is a discrete variable and z is a continuous variable. | 
		
	
		
			
				|  |  |  |  |  * m is binary and depending on m, we have 2 different means | 
		
	
		
			
				|  |  |  |  |  * μ1 and μ2 for the Gaussian distribution around which we sample z. | 
		
	
		
			
				|  |  |  |  |  * | 
		
	
		
			
				|  |  |  |  |  * The resulting factor graph should eliminate to a Bayes net | 
		
	
		
			
				|  |  |  |  |  * which represents a sigmoid function. | 
		
	
		
			
				|  |  |  |  |  */ | 
		
	
		
			
				|  |  |  |  | TEST(GaussianMixtureFactor, GaussianMixtureModel) { | 
		
	
		
			
				|  |  |  |  |   double mu0 = 1.0, mu1 = 3.0; | 
		
	
		
			
				|  |  |  |  |   double sigma = 2.0; | 
		
	
		
			
				|  |  |  |  |   auto model = noiseModel::Isotropic::Sigma(1, sigma); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   DiscreteKey m(M(0), 2); | 
		
	
		
			
				|  |  |  |  |   Key z = Z(0); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   auto c0 = make_shared<GaussianConditional>(z, Vector1(mu0), I_1x1, model), | 
		
	
		
			
				|  |  |  |  |        c1 = make_shared<GaussianConditional>(z, Vector1(mu1), I_1x1, model); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   auto gm = new GaussianMixture({z}, {}, {m}, {c0, c1}); | 
		
	
		
			
				|  |  |  |  |   auto mixing = new DiscreteConditional(m, "0.5/0.5"); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   HybridBayesNet hbn; | 
		
	
		
			
				|  |  |  |  |   hbn.emplace_back(gm); | 
		
	
		
			
				|  |  |  |  |   hbn.emplace_back(mixing); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   // The result should be a sigmoid.
 | 
		
	
		
			
				|  |  |  |  |   // So should be m = 0.5 at z=3.0 - 1.0=2.0
 | 
		
	
		
			
				|  |  |  |  |   VectorValues given; | 
		
	
		
			
				|  |  |  |  |   given.insert(z, Vector1(mu1 - mu0)); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   HybridGaussianFactorGraph gfg = hbn.toFactorGraph(given); | 
		
	
		
			
				|  |  |  |  |   HybridBayesNet::shared_ptr bn = gfg.eliminateSequential(); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   HybridBayesNet expected; | 
		
	
		
			
				|  |  |  |  |   expected.emplace_back(new DiscreteConditional(m, "0.5/0.5")); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   EXPECT(assert_equal(expected, *bn)); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | /* ************************************************************************* */ | 
		
	
		
			
				|  |  |  |  | /**
 | 
		
	
		
			
				|  |  |  |  |  * Test a simple Gaussian Mixture Model represented as P(m)P(z|m) | 
		
	
		
			
				|  |  |  |  |  * where m is a discrete variable and z is a continuous variable. | 
		
	
		
			
				|  |  |  |  |  * m is binary and depending on m, we have 2 different means | 
		
	
		
			
				|  |  |  |  |  * and covariances each for the | 
		
	
		
			
				|  |  |  |  |  * Gaussian distribution around which we sample z. | 
		
	
		
			
				|  |  |  |  |  * | 
		
	
		
			
				|  |  |  |  |  * The resulting factor graph should eliminate to a Bayes net | 
		
	
		
			
				|  |  |  |  |  * which represents a sigmoid function leaning towards | 
		
	
		
			
				|  |  |  |  |  * the tighter covariance Gaussian. | 
		
	
		
			
				|  |  |  |  |  */ | 
		
	
		
			
				|  |  |  |  | TEST(GaussianMixtureFactor, GaussianMixtureModel2) { | 
		
	
		
			
				|  |  |  |  |   double mu0 = 1.0, mu1 = 3.0; | 
		
	
		
			
				|  |  |  |  |   auto model0 = noiseModel::Isotropic::Sigma(1, 8.0); | 
		
	
		
			
				|  |  |  |  |   auto model1 = noiseModel::Isotropic::Sigma(1, 4.0); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   DiscreteKey m(M(0), 2); | 
		
	
		
			
				|  |  |  |  |   Key z = Z(0); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   auto c0 = make_shared<GaussianConditional>(z, Vector1(mu0), I_1x1, model0), | 
		
	
		
			
				|  |  |  |  |        c1 = make_shared<GaussianConditional>(z, Vector1(mu1), I_1x1, model1); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   auto gm = new GaussianMixture({z}, {}, {m}, {c0, c1}); | 
		
	
		
			
				|  |  |  |  |   auto mixing = new DiscreteConditional(m, "0.5/0.5"); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   HybridBayesNet hbn; | 
		
	
		
			
				|  |  |  |  |   hbn.emplace_back(gm); | 
		
	
		
			
				|  |  |  |  |   hbn.emplace_back(mixing); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   // The result should be a sigmoid leaning towards model1
 | 
		
	
		
			
				|  |  |  |  |   // since it has the tighter covariance.
 | 
		
	
		
			
				|  |  |  |  |   // So should be m = 0.34/0.66 at z=3.0 - 1.0=2.0
 | 
		
	
		
			
				|  |  |  |  |   VectorValues given; | 
		
	
		
			
				|  |  |  |  |   given.insert(z, Vector1(mu1 - mu0)); | 
		
	
		
			
				|  |  |  |  |   HybridGaussianFactorGraph gfg = hbn.toFactorGraph(given); | 
		
	
		
			
				|  |  |  |  |   HybridBayesNet::shared_ptr bn = gfg.eliminateSequential(); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   HybridBayesNet expected; | 
		
	
		
			
				|  |  |  |  |   expected.emplace_back( | 
		
	
		
			
				|  |  |  |  |       new DiscreteConditional(m, "0.338561851224/0.661438148776")); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   EXPECT(assert_equal(expected, *bn)); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | /* ************************************************************************* */ | 
		
	
		
			
				|  |  |  |  | /**
 | 
		
	
		
			
				|  |  |  |  |  * Test a model P(x0)P(z0|x0)p(x1|m1)p(z1|x1)p(m1). | 
		
	
		
			
				|  |  |  |  |  * | 
		
	
		
			
				|  |  |  |  |  * p(x1|m1) has different means and same covariance. | 
		
	
		
			
				|  |  |  |  |  * | 
		
	
		
			
				|  |  |  |  |  * Converting to a factor graph gives us | 
		
	
		
			
				|  |  |  |  |  * P(x0)ϕ(x0)P(x1|m1)ϕ(x1)P(m1) | 
		
	
		
			
				|  |  |  |  |  * | 
		
	
		
			
				|  |  |  |  |  * If we only have a measurement on z0, then | 
		
	
		
			
				|  |  |  |  |  * the probability of x1 should be 0.5/0.5. | 
		
	
		
			
				|  |  |  |  |  * Getting a measurement on z1 gives use more information. | 
		
	
		
			
				|  |  |  |  |  */ | 
		
	
		
			
				|  |  |  |  | TEST(GaussianMixtureFactor, TwoStateModel) { | 
		
	
		
			
				|  |  |  |  |   double mu0 = 1.0, mu1 = 3.0; | 
		
	
		
			
				|  |  |  |  |   auto model = noiseModel::Isotropic::Sigma(1, 2.0); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   DiscreteKey m1(M(1), 2); | 
		
	
		
			
				|  |  |  |  |   Key z0 = Z(0), z1 = Z(1), x0 = X(0), x1 = X(1); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   auto c0 = make_shared<GaussianConditional>(x1, Vector1(mu0), I_1x1, model), | 
		
	
		
			
				|  |  |  |  |        c1 = make_shared<GaussianConditional>(x1, Vector1(mu1), I_1x1, model); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   auto p_x0 = new GaussianConditional(x0, Vector1(0.0), I_1x1, | 
		
	
		
			
				|  |  |  |  |                                       noiseModel::Isotropic::Sigma(1, 1.0)); | 
		
	
		
			
				|  |  |  |  |   auto p_z0x0 = new GaussianConditional(z0, Vector1(0.0), I_1x1, x0, -I_1x1, | 
		
	
		
			
				|  |  |  |  |                                         noiseModel::Isotropic::Sigma(1, 1.0)); | 
		
	
		
			
				|  |  |  |  |   auto p_x1m1 = new GaussianMixture({x1}, {}, {m1}, {c0, c1}); | 
		
	
		
			
				|  |  |  |  |   auto p_z1x1 = new GaussianConditional(z1, Vector1(0.0), I_1x1, x1, -I_1x1, | 
		
	
		
			
				|  |  |  |  |                                         noiseModel::Isotropic::Sigma(1, 3.0)); | 
		
	
		
			
				|  |  |  |  |   auto p_m1 = new DiscreteConditional(m1, "0.5/0.5"); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   HybridBayesNet hbn; | 
		
	
		
			
				|  |  |  |  |   hbn.emplace_back(p_x0); | 
		
	
		
			
				|  |  |  |  |   hbn.emplace_back(p_z0x0); | 
		
	
		
			
				|  |  |  |  |   hbn.emplace_back(p_x1m1); | 
		
	
		
			
				|  |  |  |  |   hbn.emplace_back(p_m1); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VectorValues given; | 
		
	
		
			
				|  |  |  |  |   given.insert(z0, Vector1(0.5)); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   { | 
		
	
		
			
				|  |  |  |  |     // Start with no measurement on x1, only on x0
 | 
		
	
		
			
				|  |  |  |  |     HybridGaussianFactorGraph gfg = hbn.toFactorGraph(given); | 
		
	
		
			
				|  |  |  |  |     HybridBayesNet::shared_ptr bn = gfg.eliminateSequential(); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // Since no measurement on x1, we hedge our bets
 | 
		
	
		
			
				|  |  |  |  |     DiscreteConditional expected(m1, "0.5/0.5"); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     EXPECT(assert_equal(expected, *(bn->at(2)->asDiscrete()))); | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   { | 
		
	
		
			
				|  |  |  |  |     // Now we add a measurement z1 on x1
 | 
		
	
		
			
				|  |  |  |  |     hbn.emplace_back(p_z1x1); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     given.insert(z1, Vector1(2.2)); | 
		
	
		
			
				|  |  |  |  |     HybridGaussianFactorGraph gfg = hbn.toFactorGraph(given); | 
		
	
		
			
				|  |  |  |  |     HybridBayesNet::shared_ptr bn = gfg.eliminateSequential(); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // Since we have a measurement on z2, we get a definite result
 | 
		
	
		
			
				|  |  |  |  |     DiscreteConditional expected(m1, "0.4923083/0.5076917"); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     EXPECT(assert_equal(expected, *(bn->at(2)->asDiscrete()), 1e-6)); | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | /* ************************************************************************* */ | 
		
	
		
			
				|  |  |  |  | /**
 | 
		
	
		
			
				|  |  |  |  |  * Test a model P(x0)P(z0|x0)p(x1|m1)p(z1|x1)p(m1). | 
		
	
		
			
				|  |  |  |  |  * | 
		
	
		
			
				|  |  |  |  |  * p(x1|m1) has different means and different covariances. | 
		
	
		
			
				|  |  |  |  |  * | 
		
	
		
			
				|  |  |  |  |  * Converting to a factor graph gives us | 
		
	
		
			
				|  |  |  |  |  * P(x0)ϕ(x0)P(x1|m1)ϕ(x1)P(m1) | 
		
	
		
			
				|  |  |  |  |  * | 
		
	
		
			
				|  |  |  |  |  * If we only have a measurement on z0, then | 
		
	
		
			
				|  |  |  |  |  * the probability of x1 should be the ratio of covariances. | 
		
	
		
			
				|  |  |  |  |  * Getting a measurement on z1 gives use more information. | 
		
	
		
			
				|  |  |  |  |  */ | 
		
	
		
			
				|  |  |  |  | TEST(GaussianMixtureFactor, TwoStateModel2) { | 
		
	
		
			
				|  |  |  |  |   double mu0 = 1.0, mu1 = 3.0; | 
		
	
		
			
				|  |  |  |  |   auto model0 = noiseModel::Isotropic::Sigma(1, 6.0); | 
		
	
		
			
				|  |  |  |  |   auto model1 = noiseModel::Isotropic::Sigma(1, 4.0); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   DiscreteKey m1(M(1), 2); | 
		
	
		
			
				|  |  |  |  |   Key z0 = Z(0), z1 = Z(1), x0 = X(0), x1 = X(1); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   auto c0 = make_shared<GaussianConditional>(x1, Vector1(mu0), I_1x1, model0), | 
		
	
		
			
				|  |  |  |  |        c1 = make_shared<GaussianConditional>(x1, Vector1(mu1), I_1x1, model1); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   auto p_x0 = new GaussianConditional(x0, Vector1(0.0), I_1x1, | 
		
	
		
			
				|  |  |  |  |                                       noiseModel::Isotropic::Sigma(1, 1.0)); | 
		
	
		
			
				|  |  |  |  |   auto p_z0x0 = new GaussianConditional(z0, Vector1(0.0), I_1x1, x0, -I_1x1, | 
		
	
		
			
				|  |  |  |  |                                         noiseModel::Isotropic::Sigma(1, 1.0)); | 
		
	
		
			
				|  |  |  |  |   auto p_x1m1 = new GaussianMixture({x1}, {}, {m1}, {c0, c1}); | 
		
	
		
			
				|  |  |  |  |   auto p_z1x1 = new GaussianConditional(z1, Vector1(0.0), I_1x1, x1, -I_1x1, | 
		
	
		
			
				|  |  |  |  |                                         noiseModel::Isotropic::Sigma(1, 3.0)); | 
		
	
		
			
				|  |  |  |  |   auto p_m1 = new DiscreteConditional(m1, "0.5/0.5"); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   HybridBayesNet hbn; | 
		
	
		
			
				|  |  |  |  |   hbn.emplace_back(p_x0); | 
		
	
		
			
				|  |  |  |  |   hbn.emplace_back(p_z0x0); | 
		
	
		
			
				|  |  |  |  |   hbn.emplace_back(p_x1m1); | 
		
	
		
			
				|  |  |  |  |   hbn.emplace_back(p_m1); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   VectorValues given; | 
		
	
		
			
				|  |  |  |  |   given.insert(z0, Vector1(0.5)); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   { | 
		
	
		
			
				|  |  |  |  |     // Start with no measurement on x1, only on x0
 | 
		
	
		
			
				|  |  |  |  |     HybridGaussianFactorGraph gfg = hbn.toFactorGraph(given); | 
		
	
		
			
				|  |  |  |  |     HybridBayesNet::shared_ptr bn = gfg.eliminateSequential(); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // Since no measurement on x1, we get the ratio of covariances.
 | 
		
	
		
			
				|  |  |  |  |     DiscreteConditional expected(m1, "0.6/0.4"); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     EXPECT(assert_equal(expected, *(bn->at(2)->asDiscrete()))); | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   { | 
		
	
		
			
				|  |  |  |  |     // Now we add a measurement z1 on x1
 | 
		
	
		
			
				|  |  |  |  |     hbn.emplace_back(p_z1x1); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     given.insert(z1, Vector1(2.2)); | 
		
	
		
			
				|  |  |  |  |     HybridGaussianFactorGraph gfg = hbn.toFactorGraph(given); | 
		
	
		
			
				|  |  |  |  |     HybridBayesNet::shared_ptr bn = gfg.eliminateSequential(); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // Since we have a measurement on z2, we get a definite result
 | 
		
	
		
			
				|  |  |  |  |     DiscreteConditional expected(m1, "0.52706646/0.47293354"); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     EXPECT(assert_equal(expected, *(bn->at(2)->asDiscrete()), 1e-6)); | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | /**
 | 
		
	
		
			
				|  |  |  |  |  * @brief Helper function to specify a Hybrid Bayes Net | 
		
	
		
			
				|  |  |  |  |  * {P(X1) P(Z1 | X1, X2, M1)} and convert it to a Hybrid Factor Graph | 
		
	
	
		
			
				
					|  |  |  | @ -269,9 +491,10 @@ HybridGaussianFactorGraph GetFactorGraphFromBayesNet( | 
		
	
		
			
				|  |  |  |  |  * | 
		
	
		
			
				|  |  |  |  |  * We specify a hybrid Bayes network P(Z | X, M) =p(X1)p(Z1 | X1, X2, M1), | 
		
	
		
			
				|  |  |  |  |  * which is then converted to a factor graph by specifying Z1. | 
		
	
		
			
				|  |  |  |  |  * | 
		
	
		
			
				|  |  |  |  |  * p(Z1 | X1, X2, M1) has 2 factors each for the binary mode m1, with only the | 
		
	
		
			
				|  |  |  |  |  * means being different. | 
		
	
		
			
				|  |  |  |  |  * This is a different case since now we have a hybrid factor | 
		
	
		
			
				|  |  |  |  |  * with 2 continuous variables ϕ(x1, x2, m1). | 
		
	
		
			
				|  |  |  |  |  * p(Z1 | X1, X2, M1) has 2 factors each for the binary | 
		
	
		
			
				|  |  |  |  |  * mode m1, with only the means being different. | 
		
	
		
			
				|  |  |  |  |  */ | 
		
	
		
			
				|  |  |  |  | TEST(GaussianMixtureFactor, DifferentMeans) { | 
		
	
		
			
				|  |  |  |  |   DiscreteKey m1(M(1), 2); | 
		
	
	
		
			
				
					|  |  |  | @ -353,6 +576,8 @@ TEST(GaussianMixtureFactor, DifferentMeans) { | 
		
	
		
			
				|  |  |  |  | /**
 | 
		
	
		
			
				|  |  |  |  |  * @brief Test components with differing covariances | 
		
	
		
			
				|  |  |  |  |  * but with a Bayes net P(Z|X, M) converted to a FG. | 
		
	
		
			
				|  |  |  |  |  * Same as the DifferentMeans example but in this case, | 
		
	
		
			
				|  |  |  |  |  * we keep the means the same and vary the covariances. | 
		
	
		
			
				|  |  |  |  |  */ | 
		
	
		
			
				|  |  |  |  | TEST(GaussianMixtureFactor, DifferentCovariances) { | 
		
	
		
			
				|  |  |  |  |   DiscreteKey m1(M(1), 2); | 
		
	
	
		
			
				
					|  |  |  | 
 |