2 GTSAM Copyright 2010-2019, Georgia Tech Research Corporation, 3 Atlanta, Georgia 30332-0415 6 See LICENSE for the license information 8 Unit tests for Hybrid Factor Graphs. 9 Author: Fan Jiang, Varun Agrawal, Frank Dellaert 20 from gtsam
import (DiscreteConditional, DiscreteKeys, GaussianConditional,
21 GaussianMixture, GaussianMixtureFactor, HybridBayesNet,
22 HybridGaussianFactorGraph, HybridValues, JacobianFactor,
25 DEBUG_MARGINALS =
False 29 """Unit tests for HybridGaussianFactorGraph.""" 31 """Test construction of hybrid factor graph.""" 32 model = noiseModel.Unit.Create(3)
34 dk.push_back((
C(0), 2))
39 gmf = GaussianMixtureFactor([
X(0)], dk, [jf1, jf2])
41 hfg = HybridGaussianFactorGraph()
46 hbn = hfg.eliminateSequential()
48 self.assertEqual(hbn.size(), 2)
50 mixture = hbn.at(0).inner()
51 self.assertIsInstance(mixture, GaussianMixture)
52 self.assertEqual(
len(mixture.keys()), 2)
54 discrete_conditional = hbn.at(hbn.size() - 1).inner()
55 self.assertIsInstance(discrete_conditional, DiscreteConditional)
58 """Test construction of hybrid factor graph.""" 59 model = noiseModel.Unit.Create(3)
61 dk.push_back((
C(0), 2))
66 gmf = GaussianMixtureFactor([
X(0)], dk, [jf1, jf2])
68 hfg = HybridGaussianFactorGraph()
76 hbn = hfg.eliminateSequential()
79 self.assertEqual(hv.atDiscrete(
C(0)), 1)
82 def tiny(num_measurements: int = 1,
83 prior_mean: float = 5.0,
84 prior_sigma: float = 0.5) -> HybridBayesNet:
86 Create a tiny two variable hybrid model which represents 87 the generative probability P(Z, x0, mode) = P(Z|x0, mode)P(x0)P(mode). 88 num_measurements: number of measurements in Z = {z0, z1...} 91 bayesNet = HybridBayesNet()
100 for i
in range(num_measurements):
101 conditional0 = GaussianConditional.FromMeanAndStddev(
Z(i),
105 conditional1 = GaussianConditional.FromMeanAndStddev(
Z(i),
109 bayesNet.push_back(GaussianMixture([
Z(i)], [
X(0)], keys,
110 [conditional0, conditional1]))
113 prior_on_x0 = GaussianConditional.FromMeanAndStddev(
114 X(0), [prior_mean], prior_sigma)
115 bayesNet.push_back(prior_on_x0)
118 bayesNet.push_back(DiscreteConditional(mode,
"4/6"))
123 """Test evaluate with two different prior noise models.""" 126 bayesNet1 = self.
tiny(prior_sigma=0.5, num_measurements=1)
127 bayesNet2 = self.
tiny(prior_sigma=5.0, num_measurements=1)
130 expected_ratio = np.sqrt(2 * np.pi * 5.0**2) / np.sqrt(
133 mean0.insert(
X(0), [5.0])
134 mean0.insert(
Z(0), [5.0])
135 mean0.insert(
M(0), 0)
136 self.assertAlmostEqual(bayesNet1.evaluate(mean0) /
137 bayesNet2.evaluate(mean0),
141 mean1.insert(
X(0), [5.0])
142 mean1.insert(
Z(0), [5.0])
143 mean1.insert(
M(0), 1)
144 self.assertAlmostEqual(bayesNet1.evaluate(mean1) /
145 bayesNet2.evaluate(mean1),
151 """Create measurements from a sample, grabbing Z(i) for indices.""" 154 measurements.insert(
Z(i), sample.at(
Z(i)))
160 proposal_density: HybridBayesNet,
162 """Do importance sampling to estimate discrete marginal P(mode).""" 164 marginals = np.zeros((2, ))
168 proposed = proposal_density.sample()
169 target_proposed = target(proposed)
170 weight = target_proposed / proposal_density.evaluate(proposed)
171 marginals[proposed.atDiscrete(
M(0))] += weight
174 marginals /= marginals.sum()
178 """Test a tiny two variable hybrid model.""" 181 bayesNet = self.
tiny(prior_sigma=prior_sigma)
185 values.insert(
X(0), [5.0])
186 values.insert(
M(0), 0)
188 measurements.insert(
Z(0), [5.0])
189 values.insert(measurements)
191 def unnormalized_posterior(x):
192 """Posterior is proportional to joint, centered at 5.0 as well.""" 193 x.insert(measurements)
194 return bayesNet.evaluate(x)
197 posterior_information = 1 / (prior_sigma**2) + 1 / (0.5**2)
198 posterior_sigma = posterior_information**(-0.5)
199 proposal_density = self.
tiny(num_measurements=0,
201 prior_sigma=posterior_sigma)
205 proposal_density=proposal_density)
207 print(f
"True mode: {values.atDiscrete(M(0))}")
208 print(f
"P(mode=0; Z) = {marginals[0]}")
209 print(f
"P(mode=1; Z) = {marginals[1]}")
212 self.assertAlmostEqual(marginals[0], 0.74, delta=0.01)
213 self.assertAlmostEqual(marginals[1], 0.26, delta=0.01)
216 fg = bayesNet.toFactorGraph(measurements)
217 self.assertEqual(fg.size(), 3)
221 values.insert_or_assign(
M(0), mode)
222 self.assertAlmostEqual(bayesNet.evaluate(values) /
223 np.exp(-fg.error(values)),
225 self.assertAlmostEqual(bayesNet.error(values), fg.error(values))
228 posterior = fg.eliminateSequential()
230 def true_posterior(x):
231 """Posterior from elimination.""" 232 x.insert(measurements)
233 return posterior.evaluate(x)
237 proposal_density=proposal_density)
239 print(f
"True mode: {values.atDiscrete(M(0))}")
240 print(f
"P(mode=0; z0) = {marginals[0]}")
241 print(f
"P(mode=1; z0) = {marginals[1]}")
244 self.assertAlmostEqual(marginals[0], 0.74, delta=0.01)
245 self.assertAlmostEqual(marginals[1], 0.26, delta=0.01)
249 fg: HybridGaussianFactorGraph, sample: HybridValues):
250 """Calculate ratio between Bayes net and factor graph.""" 251 return bayesNet.evaluate(sample) / fg.probPrime(sample)
if \
252 fg.probPrime(sample) > 0
else 0
256 Given a tiny two variable hybrid model, with 2 measurements, test the 257 ratio of the bayes net model representing P(z,x,n)=P(z|x, n)P(x)P(n) 258 and the factor graph P(x, n | z)=P(x | n, z)P(n|z), 259 both of which represent the same posterior. 263 bayesNet = self.
tiny(prior_sigma=prior_sigma, num_measurements=2)
267 values.insert(
X(0), [5.0])
268 values.insert(
M(0), 0)
270 measurements.insert(
Z(0), [4.0])
271 measurements.insert(
Z(1), [6.0])
272 values.insert(measurements)
274 def unnormalized_posterior(x):
275 """Posterior is proportional to joint, centered at 5.0 as well.""" 276 x.insert(measurements)
277 return bayesNet.evaluate(x)
280 posterior_information = 1 / (prior_sigma**2) + 2.0 / (3.0**2)
281 posterior_sigma = posterior_information**(-0.5)
282 proposal_density = self.
tiny(num_measurements=0,
284 prior_sigma=posterior_sigma)
288 proposal_density=proposal_density)
290 print(f
"True mode: {values.atDiscrete(M(0))}")
291 print(f
"P(mode=0; Z) = {marginals[0]}")
292 print(f
"P(mode=1; Z) = {marginals[1]}")
295 self.assertAlmostEqual(marginals[0], 0.23, delta=0.01)
296 self.assertAlmostEqual(marginals[1], 0.77, delta=0.01)
299 fg = bayesNet.toFactorGraph(measurements)
300 self.assertEqual(fg.size(), 4)
308 samples = bayesNet.sample()
309 samples.update(measurements)
313 self.assertAlmostEqual(ratio, expected_ratio)
316 posterior = fg.eliminateSequential()
324 samples = posterior.sample()
325 samples.insert(measurements)
329 self.assertAlmostEqual(ratio, expected_ratio)
332 if __name__ ==
"__main__":
Matrix< RealScalar, Dynamic, Dynamic > M
EIGEN_STRONG_INLINE Packet4f print(const Packet4f &a)
Matrix< Scalar, Dynamic, Dynamic > C
Double_ range(const Point2_ &p, const Point2_ &q)
size_t len(handle h)
Get the length of a Python object.