18
18
* @date December 2021
19
19
*/
20
20
21
+ #include < gtsam/base/Testable.h>
21
22
#include < gtsam/base/TestableAssertions.h>
23
+ #include < gtsam/discrete/DiscreteConditional.h>
22
24
#include < gtsam/discrete/DiscreteValues.h>
23
25
#include < gtsam/hybrid/GaussianMixture.h>
24
26
#include < gtsam/hybrid/GaussianMixtureFactor.h>
33
35
// Include for test suite
34
36
#include < CppUnitLite/TestHarness.h>
35
37
38
+ #include < memory>
39
+
36
40
using namespace std ;
37
41
using namespace gtsam ;
38
42
using symbol_shorthand::M;
@@ -387,46 +391,89 @@ namespace test_two_state_estimation {
387
391
388
392
DiscreteKey m1 (M(1 ), 2);
389
393
390
- // / Create Two State Bayes Network with measurements
391
- static HybridBayesNet CreateBayesNet (double mu0, double mu1, double sigma0,
392
- double sigma1,
393
- bool add_second_measurement = false ,
394
- double prior_sigma = 1e-3 ,
395
- double measurement_sigma = 3.0 ) {
396
- HybridBayesNet hbn;
397
-
398
- auto measurement_model = noiseModel::Isotropic::Sigma (1 , measurement_sigma);
399
- // Add measurement P(z0 | x0)
400
- auto p_z0 = std::make_shared<GaussianConditional>(
401
- Z (0 ), Vector1 (0.0 ), -I_1x1, X (0 ), I_1x1, measurement_model);
402
- hbn.push_back (p_z0);
403
-
404
- // Add hybrid motion model
394
+ // / Create hybrid motion model p(x1 | x0, m1)
395
+ static GaussianMixture::shared_ptr CreateHybridMotionModel (double mu0,
396
+ double mu1,
397
+ double sigma0,
398
+ double sigma1) {
405
399
auto model0 = noiseModel::Isotropic::Sigma (1 , sigma0);
406
400
auto model1 = noiseModel::Isotropic::Sigma (1 , sigma1);
407
401
auto c0 = make_shared<GaussianConditional>(X (1 ), Vector1 (mu0), I_1x1, X (0 ),
408
402
-I_1x1, model0),
409
403
c1 = make_shared<GaussianConditional>(X (1 ), Vector1 (mu1), I_1x1, X (0 ),
410
404
-I_1x1, model1);
411
-
412
- auto motion = std::make_shared<GaussianMixture>(
405
+ return std::make_shared<GaussianMixture>(
413
406
KeyVector{X (1 )}, KeyVector{X (0 )}, DiscreteKeys{m1}, std::vector{c0, c1});
414
- hbn.push_back (motion);
407
+ }
408
+
409
+ // / Create two state Bayes network with 1 or two measurement models
410
+ HybridBayesNet CreateBayesNet (
411
+ const GaussianMixture::shared_ptr& hybridMotionModel,
412
+ bool add_second_measurement = false ) {
413
+ HybridBayesNet hbn;
415
414
415
+ // Add measurement model p(z0 | x0)
416
+ const double measurement_sigma = 3.0 ;
417
+ auto measurement_model = noiseModel::Isotropic::Sigma (1 , measurement_sigma);
418
+ hbn.emplace_shared <GaussianConditional>(Z (0 ), Vector1 (0.0 ), I_1x1, X (0 ),
419
+ -I_1x1, measurement_model);
420
+
421
+ // Optionally add second measurement model p(z1 | x1)
416
422
if (add_second_measurement) {
417
- // Add second measurement
418
- auto p_z1 = std::make_shared<GaussianConditional>(
419
- Z (1 ), Vector1 (0.0 ), -I_1x1, X (1 ), I_1x1, measurement_model);
420
- hbn.push_back (p_z1);
423
+ hbn.emplace_shared <GaussianConditional>(Z (1 ), Vector1 (0.0 ), I_1x1, X (1 ),
424
+ -I_1x1, measurement_model);
421
425
}
422
426
427
+ // Add hybrid motion model
428
+ hbn.push_back (hybridMotionModel);
429
+
423
430
// Discrete uniform prior.
424
- auto p_m1 = std::make_shared<DiscreteConditional>(m1, " 0.5/0.5" );
425
- hbn.push_back (p_m1);
431
+ hbn.emplace_shared <DiscreteConditional>(m1, " 0.5/0.5" );
426
432
427
433
return hbn;
428
434
}
429
435
436
+ // / Create importance sampling network p(x1| x0, m1) p(x0) P(m1),
437
+ // / using Q(x0) = N(z0, sigma_Q) to sample from p(x0)
438
+ HybridBayesNet CreateProposalNet (
439
+ const GaussianMixture::shared_ptr& hybridMotionModel, double z0,
440
+ double sigma_Q) {
441
+ HybridBayesNet hbn;
442
+
443
+ // Add hybrid motion model
444
+ hbn.push_back (hybridMotionModel);
445
+
446
+ // Add proposal Q(x0) for x0
447
+ auto measurement_model = noiseModel::Isotropic::Sigma (1 , sigma_Q);
448
+ hbn.emplace_shared <GaussianConditional>(
449
+ GaussianConditional::FromMeanAndStddev (X (0 ), Vector1 (z0), sigma_Q));
450
+
451
+ // Discrete uniform prior.
452
+ hbn.emplace_shared <DiscreteConditional>(m1, " 0.5/0.5" );
453
+
454
+ return hbn;
455
+ }
456
+
457
+ // / Approximate the discrete marginal P(m1) using importance sampling
458
+ // / Not typically called as expensive, but values are used in the tests.
459
+ void approximateDiscreteMarginal (const HybridBayesNet& hbn,
460
+ const HybridBayesNet& proposalNet,
461
+ const VectorValues& given) {
462
+ // Do importance sampling
463
+ double w0 = 0.0 , w1 = 0.0 ;
464
+ std::mt19937_64 rng (44 );
465
+ for (int i = 0 ; i < 50000 ; i++) {
466
+ HybridValues sample = proposalNet.sample (&rng);
467
+ sample.insert (given);
468
+ double weight = hbn.evaluate (sample) / proposalNet.evaluate (sample);
469
+ (sample.atDiscrete (m1.first ) == 0 ) ? w0 += weight : w1 += weight;
470
+ }
471
+ double sumWeights = w0 + w1;
472
+ double pm1 = w1 / sumWeights;
473
+ std::cout << " p(m0) ~ " << 1.0 - pm1 << std::endl;
474
+ std::cout << " p(m1) ~ " << pm1 << std::endl;
475
+ }
476
+
430
477
} // namespace test_two_state_estimation
431
478
432
479
/* ************************************************************************* */
@@ -446,37 +493,48 @@ TEST(GaussianMixtureFactor, TwoStateModel) {
446
493
using namespace test_two_state_estimation ;
447
494
448
495
double mu0 = 1.0 , mu1 = 3.0 ;
449
- double sigma = 2.0 ;
496
+ double sigma = 0.5 ;
497
+ auto hybridMotionModel = CreateHybridMotionModel (mu0, mu1, sigma, sigma);
450
498
451
499
// Start with no measurement on x1, only on x0
452
- HybridBayesNet hbn = CreateBayesNet (mu0, mu1, sigma, sigma, false ) ;
500
+ double z0 = 0.5 ;
453
501
454
502
VectorValues given;
455
- given.insert (Z (0 ), Vector1 (0.5 ));
503
+ given.insert (Z (0 ), Vector1 (z0));
504
+
505
+ // Create proposal network for importance sampling
506
+ auto proposalNet = CreateProposalNet (hybridMotionModel, z0, 3.0 );
507
+ EXPECT_LONGS_EQUAL (3 , proposalNet.size ());
456
508
457
509
{
510
+ HybridBayesNet hbn = CreateBayesNet (hybridMotionModel);
458
511
HybridGaussianFactorGraph gfg = hbn.toFactorGraph (given);
459
512
HybridBayesNet::shared_ptr bn = gfg.eliminateSequential ();
460
513
461
514
// Since no measurement on x1, we hedge our bets
515
+ // Importance sampling run with 50k samples gives 0.49934/0.50066
516
+ // approximateDiscreteMarginal(hbn, proposalNet, given);
462
517
DiscreteConditional expected (m1, " 0.5/0.5" );
463
518
EXPECT (assert_equal (expected, *(bn->at (2 )->asDiscrete ())));
464
519
}
465
520
466
521
{
467
522
// Now we add a measurement z1 on x1
468
- hbn = CreateBayesNet (mu0, mu1, sigma, sigma , true );
523
+ HybridBayesNet hbn = CreateBayesNet (hybridMotionModel , true );
469
524
470
- // If we see z1=2.6 ( > 2.5 which is the halfway point),
525
+ // If we see z1=4.5 (> > 2.5 which is the halfway point),
471
526
// discrete mode should say m1=1
472
- given.insert (Z (1 ), Vector1 (2.6 ));
527
+ const double z1 = 4.5 ;
528
+ given.insert (Z (1 ), Vector1 (z1));
529
+
473
530
HybridGaussianFactorGraph gfg = hbn.toFactorGraph (given);
474
531
HybridBayesNet::shared_ptr bn = gfg.eliminateSequential ();
475
532
476
- // Since we have a measurement on z2, we get a definite result
477
- DiscreteConditional expected (m1, " 0.49772729/0.50227271" );
478
- // regression
479
- EXPECT (assert_equal (expected, *(bn->at (2 )->asDiscrete ()), 1e-6 ));
533
+ // Since we have a measurement on x1, we get a definite result
534
+ // Values taken from an importance sampling run with 50k samples:
535
+ // approximateDiscreteMarginal(hbn, proposalNet, given);
536
+ DiscreteConditional expected (m1, " 0.446629/0.553371" );
537
+ EXPECT (assert_equal (expected, *(bn->at (2 )->asDiscrete ()), 0.01 ));
480
538
}
481
539
}
482
540
@@ -498,30 +556,32 @@ TEST(GaussianMixtureFactor, TwoStateModel2) {
498
556
499
557
double mu0 = 1.0 , mu1 = 3.0 ;
500
558
double sigma0 = 6.0 , sigma1 = 4.0 ;
501
- auto model0 = noiseModel::Isotropic::Sigma (1 , sigma0);
502
- auto model1 = noiseModel::Isotropic::Sigma (1 , sigma1);
559
+ auto hybridMotionModel = CreateHybridMotionModel (mu0, mu1, sigma0, sigma1);
503
560
504
561
// Start with no measurement on x1, only on x0
505
- HybridBayesNet hbn = CreateBayesNet (mu0, mu1, sigma0, sigma1, false );
506
-
562
+ double z0 = 0.5 ;
507
563
VectorValues given;
508
- given.insert (Z (0 ), Vector1 (0.5 ));
564
+ given.insert (Z (0 ), Vector1 (z0));
565
+
566
+ // Create proposal network for importance sampling
567
+ // uncomment this and the approximateDiscreteMarginal calls to run
568
+ // auto proposalNet = CreateProposalNet(hybridMotionModel, z0, 3.0);
509
569
510
570
{
511
- // Start with no measurement on x1, only on x0
571
+ HybridBayesNet hbn = CreateBayesNet (hybridMotionModel);
512
572
HybridGaussianFactorGraph gfg = hbn.toFactorGraph (given);
513
573
514
574
{
515
575
VectorValues vv{
516
- {X (0 ), Vector1 (0.0 )}, {X (1 ), Vector1 (1.0 )}, {Z (0 ), Vector1 (0.5 )}};
576
+ {X (0 ), Vector1 (0.0 )}, {X (1 ), Vector1 (1.0 )}, {Z (0 ), Vector1 (z0 )}};
517
577
HybridValues hv0 (vv, DiscreteValues{{M (1 ), 0 }}),
518
578
hv1 (vv, DiscreteValues{{M (1 ), 1 }});
519
579
EXPECT_DOUBLES_EQUAL (gfg.error (hv0) / hbn.error (hv0),
520
580
gfg.error (hv1) / hbn.error (hv1), 1e-9 );
521
581
}
522
582
{
523
583
VectorValues vv{
524
- {X (0 ), Vector1 (0.5 )}, {X (1 ), Vector1 (3.0 )}, {Z (0 ), Vector1 (0.5 )}};
584
+ {X (0 ), Vector1 (0.5 )}, {X (1 ), Vector1 (3.0 )}, {Z (0 ), Vector1 (z0 )}};
525
585
HybridValues hv0 (vv, DiscreteValues{{M (1 ), 0 }}),
526
586
hv1 (vv, DiscreteValues{{M (1 ), 1 }});
527
587
EXPECT_DOUBLES_EQUAL (gfg.error (hv0) / hbn.error (hv0),
@@ -530,6 +590,9 @@ TEST(GaussianMixtureFactor, TwoStateModel2) {
530
590
531
591
HybridBayesNet::shared_ptr bn = gfg.eliminateSequential ();
532
592
593
+ // Importance sampling run with 50k samples gives 0.49934/0.50066
594
+ // approximateDiscreteMarginal(hbn, proposalNet, given);
595
+
533
596
// Since no measurement on x1, we a 50/50 probability
534
597
auto p_m = bn->at (2 )->asDiscrete ();
535
598
EXPECT_DOUBLES_EQUAL (0.5 , p_m->operator ()(DiscreteValues{{m1.first , 0 }}),
@@ -540,16 +603,18 @@ TEST(GaussianMixtureFactor, TwoStateModel2) {
540
603
541
604
{
542
605
// Now we add a measurement z1 on x1
543
- hbn = CreateBayesNet (mu0, mu1, sigma0, sigma1, true );
606
+ HybridBayesNet hbn = CreateBayesNet (hybridMotionModel, true );
607
+
608
+ double z1 = 2.2 ;
609
+ given.insert (Z (1 ), Vector1 (z1));
544
610
545
- given.insert (Z (1 ), Vector1 (2.2 ));
546
611
HybridGaussianFactorGraph gfg = hbn.toFactorGraph (given);
547
612
548
613
{
549
614
VectorValues vv{{X (0 ), Vector1 (0.0 )},
550
615
{X (1 ), Vector1 (1.0 )},
551
- {Z (0 ), Vector1 (0.5 )},
552
- {Z (1 ), Vector1 (2.2 )}};
616
+ {Z (0 ), Vector1 (z0 )},
617
+ {Z (1 ), Vector1 (z1 )}};
553
618
HybridValues hv0 (vv, DiscreteValues{{M (1 ), 0 }}),
554
619
hv1 (vv, DiscreteValues{{M (1 ), 1 }});
555
620
EXPECT_DOUBLES_EQUAL (gfg.error (hv0) / hbn.error (hv0),
@@ -558,8 +623,8 @@ TEST(GaussianMixtureFactor, TwoStateModel2) {
558
623
{
559
624
VectorValues vv{{X (0 ), Vector1 (0.5 )},
560
625
{X (1 ), Vector1 (3.0 )},
561
- {Z (0 ), Vector1 (0.5 )},
562
- {Z (1 ), Vector1 (2.2 )}};
626
+ {Z (0 ), Vector1 (z0 )},
627
+ {Z (1 ), Vector1 (z1 )}};
563
628
HybridValues hv0 (vv, DiscreteValues{{M (1 ), 0 }}),
564
629
hv1 (vv, DiscreteValues{{M (1 ), 1 }});
565
630
EXPECT_DOUBLES_EQUAL (gfg.error (hv0) / hbn.error (hv0),
@@ -569,9 +634,10 @@ TEST(GaussianMixtureFactor, TwoStateModel2) {
569
634
HybridBayesNet::shared_ptr bn = gfg.eliminateSequential ();
570
635
571
636
// Since we have a measurement on z2, we get a definite result
572
- DiscreteConditional expected (m1, " 0.44744586/0.55255414" );
573
- // regression
574
- EXPECT (assert_equal (expected, *(bn->at (2 )->asDiscrete ()), 1e-6 ));
637
+ // Values taken from an importance sampling run with 50k samples:
638
+ // approximateDiscreteMarginal(hbn, proposalNet, given);
639
+ DiscreteConditional expected (m1, " 0.446345/0.553655" );
640
+ EXPECT (assert_equal (expected, *(bn->at (2 )->asDiscrete ()), 0.01 ));
575
641
}
576
642
}
577
643
0 commit comments