commit
						63c4e33e8c
					
				| 
						 | 
				
			
			@ -49,7 +49,7 @@ std::function<double(const Assignment<Key> &, double)> prunerFunc(
 | 
			
		|||
    const DecisionTreeFactor &prunedDiscreteProbs,
 | 
			
		||||
    const HybridConditional &conditional) {
 | 
			
		||||
  // Get the discrete keys as sets for the decision tree
 | 
			
		||||
  // and the Gaussian mixture.
 | 
			
		||||
  // and the hybrid Gaussian conditional.
 | 
			
		||||
  std::set<DiscreteKey> discreteProbsKeySet =
 | 
			
		||||
      DiscreteKeysAsSet(prunedDiscreteProbs.discreteKeys());
 | 
			
		||||
  std::set<DiscreteKey> conditionalKeySet =
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +63,7 @@ std::function<double(const Assignment<Key> &, double)> prunerFunc(
 | 
			
		|||
 | 
			
		||||
    // typecast so we can use this to get probability value
 | 
			
		||||
    DiscreteValues values(choices);
 | 
			
		||||
    // Case where the Gaussian mixture has the same
 | 
			
		||||
    // Case where the hybrid Gaussian conditional has the same
 | 
			
		||||
    // discrete keys as the decision tree.
 | 
			
		||||
    if (conditionalKeySet == discreteProbsKeySet) {
 | 
			
		||||
      if (prunedDiscreteProbs(values) == 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -180,8 +180,8 @@ HybridBayesNet HybridBayesNet::prune(size_t maxNrLeaves) {
 | 
			
		|||
  // Go through all the conditionals in the
 | 
			
		||||
  // Bayes Net and prune them as per prunedDiscreteProbs.
 | 
			
		||||
  for (auto &&conditional : *this) {
 | 
			
		||||
    if (auto gm = conditional->asMixture()) {
 | 
			
		||||
      // Make a copy of the Gaussian mixture and prune it!
 | 
			
		||||
    if (auto gm = conditional->asHybrid()) {
 | 
			
		||||
      // Make a copy of the hybrid Gaussian conditional and prune it!
 | 
			
		||||
      auto prunedHybridGaussianConditional =
 | 
			
		||||
          std::make_shared<HybridGaussianConditional>(*gm);
 | 
			
		||||
      prunedHybridGaussianConditional->prune(
 | 
			
		||||
| 
						 | 
				
			
			@ -204,7 +204,7 @@ GaussianBayesNet HybridBayesNet::choose(
 | 
			
		|||
    const DiscreteValues &assignment) const {
 | 
			
		||||
  GaussianBayesNet gbn;
 | 
			
		||||
  for (auto &&conditional : *this) {
 | 
			
		||||
    if (auto gm = conditional->asMixture()) {
 | 
			
		||||
    if (auto gm = conditional->asHybrid()) {
 | 
			
		||||
      // If conditional is hybrid, select based on assignment.
 | 
			
		||||
      gbn.push_back((*gm)(assignment));
 | 
			
		||||
    } else if (auto gc = conditional->asGaussian()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -291,7 +291,7 @@ AlgebraicDecisionTree<Key> HybridBayesNet::errorTree(
 | 
			
		|||
 | 
			
		||||
  // Iterate over each conditional.
 | 
			
		||||
  for (auto &&conditional : *this) {
 | 
			
		||||
    if (auto gm = conditional->asMixture()) {
 | 
			
		||||
    if (auto gm = conditional->asHybrid()) {
 | 
			
		||||
      // If conditional is hybrid, compute error for all assignments.
 | 
			
		||||
      result = result + gm->errorTree(continuousValues);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -321,7 +321,7 @@ AlgebraicDecisionTree<Key> HybridBayesNet::logProbability(
 | 
			
		|||
 | 
			
		||||
  // Iterate over each conditional.
 | 
			
		||||
  for (auto &&conditional : *this) {
 | 
			
		||||
    if (auto gm = conditional->asMixture()) {
 | 
			
		||||
    if (auto gm = conditional->asHybrid()) {
 | 
			
		||||
      // If conditional is hybrid, select based on assignment and compute
 | 
			
		||||
      // logProbability.
 | 
			
		||||
      result = result + gm->logProbability(continuousValues);
 | 
			
		||||
| 
						 | 
				
			
			@ -369,7 +369,7 @@ HybridGaussianFactorGraph HybridBayesNet::toFactorGraph(
 | 
			
		|||
    if (conditional->frontalsIn(measurements)) {
 | 
			
		||||
      if (auto gc = conditional->asGaussian()) {
 | 
			
		||||
        fg.push_back(gc->likelihood(measurements));
 | 
			
		||||
      } else if (auto gm = conditional->asMixture()) {
 | 
			
		||||
      } else if (auto gm = conditional->asHybrid()) {
 | 
			
		||||
        fg.push_back(gm->likelihood(measurements));
 | 
			
		||||
      } else {
 | 
			
		||||
        throw std::runtime_error("Unknown conditional type");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,8 @@ namespace gtsam {
 | 
			
		|||
 | 
			
		||||
/**
 | 
			
		||||
 * A hybrid Bayes net is a collection of HybridConditionals, which can have
 | 
			
		||||
 * discrete conditionals, Gaussian mixtures, or pure Gaussian conditionals.
 | 
			
		||||
 * discrete conditionals, hybrid Gaussian conditionals,
 | 
			
		||||
 * or pure Gaussian conditionals.
 | 
			
		||||
 *
 | 
			
		||||
 * @ingroup hybrid
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -109,7 +109,7 @@ struct HybridAssignmentData {
 | 
			
		|||
 | 
			
		||||
    GaussianConditional::shared_ptr conditional;
 | 
			
		||||
    if (hybrid_conditional->isHybrid()) {
 | 
			
		||||
      conditional = (*hybrid_conditional->asMixture())(parentData.assignment_);
 | 
			
		||||
      conditional = (*hybrid_conditional->asHybrid())(parentData.assignment_);
 | 
			
		||||
    } else if (hybrid_conditional->isContinuous()) {
 | 
			
		||||
      conditional = hybrid_conditional->asGaussian();
 | 
			
		||||
    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -205,9 +205,9 @@ void HybridBayesTree::prune(const size_t maxNrLeaves) {
 | 
			
		|||
 | 
			
		||||
      // If conditional is hybrid, we prune it.
 | 
			
		||||
      if (conditional->isHybrid()) {
 | 
			
		||||
        auto gaussianMixture = conditional->asMixture();
 | 
			
		||||
        auto hybridGaussianCond = conditional->asHybrid();
 | 
			
		||||
 | 
			
		||||
        gaussianMixture->prune(parentData.prunedDiscreteProbs);
 | 
			
		||||
        hybridGaussianCond->prune(parentData.prunedDiscreteProbs);
 | 
			
		||||
      }
 | 
			
		||||
      return parentData;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -99,8 +99,8 @@ class GTSAM_EXPORT HybridBayesTree : public BayesTree<HybridBayesTreeClique> {
 | 
			
		|||
  /**
 | 
			
		||||
   * @brief Recursively optimize the BayesTree to produce a vector solution.
 | 
			
		||||
   *
 | 
			
		||||
   * @param assignment The discrete values assignment to select the Gaussian
 | 
			
		||||
   * mixtures.
 | 
			
		||||
   * @param assignment The discrete values assignment to select
 | 
			
		||||
   * the hybrid conditional.
 | 
			
		||||
   * @return VectorValues
 | 
			
		||||
   */
 | 
			
		||||
  VectorValues optimize(const DiscreteValues& assignment) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -170,7 +170,7 @@ class BayesTreeOrphanWrapper<HybridBayesTreeClique> : public HybridConditional {
 | 
			
		|||
  void print(
 | 
			
		||||
      const std::string& s = "",
 | 
			
		||||
      const KeyFormatter& formatter = DefaultKeyFormatter) const override {
 | 
			
		||||
    clique->print(s + "stored clique", formatter);
 | 
			
		||||
    clique->print(s + " stored clique ", formatter);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,11 +50,11 @@ HybridConditional::HybridConditional(
 | 
			
		|||
 | 
			
		||||
/* ************************************************************************ */
 | 
			
		||||
HybridConditional::HybridConditional(
 | 
			
		||||
    const std::shared_ptr<HybridGaussianConditional> &gaussianMixture)
 | 
			
		||||
    : BaseFactor(gaussianMixture->continuousKeys(),
 | 
			
		||||
                 gaussianMixture->discreteKeys()),
 | 
			
		||||
      BaseConditional(gaussianMixture->nrFrontals()) {
 | 
			
		||||
  inner_ = gaussianMixture;
 | 
			
		||||
    const std::shared_ptr<HybridGaussianConditional> &hybridGaussianCond)
 | 
			
		||||
    : BaseFactor(hybridGaussianCond->continuousKeys(),
 | 
			
		||||
                 hybridGaussianCond->discreteKeys()),
 | 
			
		||||
      BaseConditional(hybridGaussianCond->nrFrontals()) {
 | 
			
		||||
  inner_ = hybridGaussianCond;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************ */
 | 
			
		||||
| 
						 | 
				
			
			@ -97,8 +97,8 @@ void HybridConditional::print(const std::string &s,
 | 
			
		|||
bool HybridConditional::equals(const HybridFactor &other, double tol) const {
 | 
			
		||||
  const This *e = dynamic_cast<const This *>(&other);
 | 
			
		||||
  if (e == nullptr) return false;
 | 
			
		||||
  if (auto gm = asMixture()) {
 | 
			
		||||
    auto other = e->asMixture();
 | 
			
		||||
  if (auto gm = asHybrid()) {
 | 
			
		||||
    auto other = e->asHybrid();
 | 
			
		||||
    return other != nullptr && gm->equals(*other, tol);
 | 
			
		||||
  }
 | 
			
		||||
  if (auto gc = asGaussian()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -119,7 +119,7 @@ double HybridConditional::error(const HybridValues &values) const {
 | 
			
		|||
  if (auto gc = asGaussian()) {
 | 
			
		||||
    return gc->error(values.continuous());
 | 
			
		||||
  }
 | 
			
		||||
  if (auto gm = asMixture()) {
 | 
			
		||||
  if (auto gm = asHybrid()) {
 | 
			
		||||
    return gm->error(values);
 | 
			
		||||
  }
 | 
			
		||||
  if (auto dc = asDiscrete()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -134,7 +134,7 @@ double HybridConditional::logProbability(const HybridValues &values) const {
 | 
			
		|||
  if (auto gc = asGaussian()) {
 | 
			
		||||
    return gc->logProbability(values.continuous());
 | 
			
		||||
  }
 | 
			
		||||
  if (auto gm = asMixture()) {
 | 
			
		||||
  if (auto gm = asHybrid()) {
 | 
			
		||||
    return gm->logProbability(values);
 | 
			
		||||
  }
 | 
			
		||||
  if (auto dc = asDiscrete()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +149,7 @@ double HybridConditional::logNormalizationConstant() const {
 | 
			
		|||
  if (auto gc = asGaussian()) {
 | 
			
		||||
    return gc->logNormalizationConstant();
 | 
			
		||||
  }
 | 
			
		||||
  if (auto gm = asMixture()) {
 | 
			
		||||
  if (auto gm = asHybrid()) {
 | 
			
		||||
    return gm->logNormalizationConstant();  // 0.0!
 | 
			
		||||
  }
 | 
			
		||||
  if (auto dc = asDiscrete()) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -124,11 +124,11 @@ class GTSAM_EXPORT HybridConditional
 | 
			
		|||
  /**
 | 
			
		||||
   * @brief Construct a new Hybrid Conditional object
 | 
			
		||||
   *
 | 
			
		||||
   * @param gaussianMixture Gaussian Mixture Conditional used to create the
 | 
			
		||||
   * @param hybridGaussianCond Hybrid Gaussian Conditional used to create the
 | 
			
		||||
   * HybridConditional.
 | 
			
		||||
   */
 | 
			
		||||
  HybridConditional(
 | 
			
		||||
      const std::shared_ptr<HybridGaussianConditional>& gaussianMixture);
 | 
			
		||||
      const std::shared_ptr<HybridGaussianConditional>& hybridGaussianCond);
 | 
			
		||||
 | 
			
		||||
  /// @}
 | 
			
		||||
  /// @name Testable
 | 
			
		||||
| 
						 | 
				
			
			@ -148,10 +148,10 @@ class GTSAM_EXPORT HybridConditional
 | 
			
		|||
 | 
			
		||||
  /**
 | 
			
		||||
   * @brief Return HybridConditional as a HybridGaussianConditional
 | 
			
		||||
   * @return nullptr if not a mixture
 | 
			
		||||
   * @return nullptr if not a conditional
 | 
			
		||||
   * @return HybridGaussianConditional::shared_ptr otherwise
 | 
			
		||||
   */
 | 
			
		||||
  HybridGaussianConditional::shared_ptr asMixture() const {
 | 
			
		||||
  HybridGaussianConditional::shared_ptr asHybrid() const {
 | 
			
		||||
    return std::dynamic_pointer_cast<HybridGaussianConditional>(inner_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -249,20 +249,20 @@ std::function<GaussianConditional::shared_ptr(
 | 
			
		|||
    const Assignment<Key> &, const GaussianConditional::shared_ptr &)>
 | 
			
		||||
HybridGaussianConditional::prunerFunc(const DecisionTreeFactor &discreteProbs) {
 | 
			
		||||
  // Get the discrete keys as sets for the decision tree
 | 
			
		||||
  // and the gaussian mixture.
 | 
			
		||||
  // and the hybrid gaussian conditional.
 | 
			
		||||
  auto discreteProbsKeySet = DiscreteKeysAsSet(discreteProbs.discreteKeys());
 | 
			
		||||
  auto gaussianMixtureKeySet = DiscreteKeysAsSet(this->discreteKeys());
 | 
			
		||||
  auto hybridGaussianCondKeySet = DiscreteKeysAsSet(this->discreteKeys());
 | 
			
		||||
 | 
			
		||||
  auto pruner = [discreteProbs, discreteProbsKeySet, gaussianMixtureKeySet](
 | 
			
		||||
  auto pruner = [discreteProbs, discreteProbsKeySet, hybridGaussianCondKeySet](
 | 
			
		||||
                    const Assignment<Key> &choices,
 | 
			
		||||
                    const GaussianConditional::shared_ptr &conditional)
 | 
			
		||||
      -> GaussianConditional::shared_ptr {
 | 
			
		||||
    // typecast so we can use this to get probability value
 | 
			
		||||
    const DiscreteValues values(choices);
 | 
			
		||||
 | 
			
		||||
    // Case where the gaussian mixture has the same
 | 
			
		||||
    // Case where the hybrid gaussian conditional has the same
 | 
			
		||||
    // discrete keys as the decision tree.
 | 
			
		||||
    if (gaussianMixtureKeySet == discreteProbsKeySet) {
 | 
			
		||||
    if (hybridGaussianCondKeySet == discreteProbsKeySet) {
 | 
			
		||||
      if (discreteProbs(values) == 0.0) {
 | 
			
		||||
        // empty aka null pointer
 | 
			
		||||
        std::shared_ptr<GaussianConditional> null;
 | 
			
		||||
| 
						 | 
				
			
			@ -274,7 +274,7 @@ HybridGaussianConditional::prunerFunc(const DecisionTreeFactor &discreteProbs) {
 | 
			
		|||
      std::vector<DiscreteKey> set_diff;
 | 
			
		||||
      std::set_difference(
 | 
			
		||||
          discreteProbsKeySet.begin(), discreteProbsKeySet.end(),
 | 
			
		||||
          gaussianMixtureKeySet.begin(), gaussianMixtureKeySet.end(),
 | 
			
		||||
          hybridGaussianCondKeySet.begin(), hybridGaussianCondKeySet.end(),
 | 
			
		||||
          std::back_inserter(set_diff));
 | 
			
		||||
 | 
			
		||||
      const std::vector<DiscreteValues> assignments =
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,8 +33,8 @@ namespace gtsam {
 | 
			
		|||
class HybridValues;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief A conditional of gaussian mixtures indexed by discrete variables, as
 | 
			
		||||
 * part of a Bayes Network. This is the result of the elimination of a
 | 
			
		||||
 * @brief A conditional of gaussian conditionals indexed by discrete variables,
 | 
			
		||||
 * as part of a Bayes Network. This is the result of the elimination of a
 | 
			
		||||
 * continuous variable in a hybrid scheme, such that the remaining variables are
 | 
			
		||||
 * discrete+continuous.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -107,7 +107,7 @@ class GTSAM_EXPORT HybridGaussianConditional
 | 
			
		|||
                            const Conditionals &conditionals);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @brief Make a Gaussian Mixture from a vector of Gaussian conditionals.
 | 
			
		||||
   * @brief Make a Hybrid Gaussian Conditional from a vector of Gaussian conditionals.
 | 
			
		||||
   * The DecisionTree-based constructor is preferred over this one.
 | 
			
		||||
   *
 | 
			
		||||
   * @param continuousFrontals The continuous frontal variables
 | 
			
		||||
| 
						 | 
				
			
			@ -152,8 +152,8 @@ class GTSAM_EXPORT HybridGaussianConditional
 | 
			
		|||
  double logNormalizationConstant() const override { return logConstant_; }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Create a likelihood factor for a Gaussian mixture, return a Mixture factor
 | 
			
		||||
   * on the parents.
 | 
			
		||||
   * Create a likelihood factor for a hybrid Gaussian conditional,
 | 
			
		||||
   * return a hybrid Gaussian factor on the parents.
 | 
			
		||||
   */
 | 
			
		||||
  std::shared_ptr<HybridGaussianFactor> likelihood(
 | 
			
		||||
      const VectorValues &given) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -172,9 +172,9 @@ class GTSAM_EXPORT HybridGaussianConditional
 | 
			
		|||
      const VectorValues &continuousValues) const;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @brief Compute the error of this Gaussian Mixture.
 | 
			
		||||
   * @brief Compute the error of this hybrid Gaussian conditional.
 | 
			
		||||
   *
 | 
			
		||||
   * This requires some care, as different mixture components may have
 | 
			
		||||
   * This requires some care, as different components may have
 | 
			
		||||
   * different normalization constants. Let's consider p(x|y,m), where m is
 | 
			
		||||
   * discrete. We need the error to satisfy the invariant:
 | 
			
		||||
   *
 | 
			
		||||
| 
						 | 
				
			
			@ -209,7 +209,7 @@ class GTSAM_EXPORT HybridGaussianConditional
 | 
			
		|||
      const VectorValues &continuousValues) const;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @brief Compute the logProbability of this Gaussian Mixture.
 | 
			
		||||
   * @brief Compute the logProbability of this hybrid Gaussian conditional.
 | 
			
		||||
   *
 | 
			
		||||
   * @param values Continuous values and discrete assignment.
 | 
			
		||||
   * @return double
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,13 +37,13 @@ class VectorValues;
 | 
			
		|||
using GaussianFactorValuePair = std::pair<GaussianFactor::shared_ptr, double>;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Implementation of a discrete conditional mixture factor.
 | 
			
		||||
 * @brief Implementation of a discrete-conditioned hybrid factor.
 | 
			
		||||
 * Implements a joint discrete-continuous factor where the discrete variable
 | 
			
		||||
 * serves to "select" a mixture component corresponding to a GaussianFactor type
 | 
			
		||||
 * of measurement.
 | 
			
		||||
 * serves to "select" a component corresponding to a GaussianFactor.
 | 
			
		||||
 *
 | 
			
		||||
 * Represents the underlying Gaussian mixture as a Decision Tree, where the set
 | 
			
		||||
 * of discrete variables indexes to the continuous gaussian distribution.
 | 
			
		||||
 * Represents the underlying hybrid Gaussian components as a Decision Tree,
 | 
			
		||||
 * where the set of discrete variables indexes to
 | 
			
		||||
 * the continuous gaussian distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * @ingroup hybrid
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +80,7 @@ class GTSAM_EXPORT HybridGaussianFactor : public HybridFactor {
 | 
			
		|||
  HybridGaussianFactor() = default;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @brief Construct a new Gaussian mixture factor.
 | 
			
		||||
   * @brief Construct a new hybrid Gaussian factor.
 | 
			
		||||
   *
 | 
			
		||||
   * @param continuousKeys A vector of keys representing continuous variables.
 | 
			
		||||
   * @param discreteKeys A vector of keys representing discrete variables and
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -115,7 +115,7 @@ void HybridGaussianFactorGraph::printErrors(
 | 
			
		|||
        } else {
 | 
			
		||||
          // Is hybrid
 | 
			
		||||
          auto conditionalComponent =
 | 
			
		||||
              hc->asMixture()->operator()(values.discrete());
 | 
			
		||||
              hc->asHybrid()->operator()(values.discrete());
 | 
			
		||||
          conditionalComponent->print(ss.str(), keyFormatter);
 | 
			
		||||
          std::cout << "error = " << conditionalComponent->error(values)
 | 
			
		||||
                    << "\n";
 | 
			
		||||
| 
						 | 
				
			
			@ -184,7 +184,7 @@ GaussianFactorGraphTree HybridGaussianFactorGraph::assembleGraphTree() const {
 | 
			
		|||
    } else if (auto gm = dynamic_pointer_cast<HybridGaussianConditional>(f)) {
 | 
			
		||||
      result = gm->add(result);
 | 
			
		||||
    } else if (auto hc = dynamic_pointer_cast<HybridConditional>(f)) {
 | 
			
		||||
      if (auto gm = hc->asMixture()) {
 | 
			
		||||
      if (auto gm = hc->asHybrid()) {
 | 
			
		||||
        result = gm->add(result);
 | 
			
		||||
      } else if (auto g = hc->asGaussian()) {
 | 
			
		||||
        result = addGaussian(result, g);
 | 
			
		||||
| 
						 | 
				
			
			@ -437,8 +437,8 @@ EliminateHybrid(const HybridGaussianFactorGraph &factors,
 | 
			
		|||
                const Ordering &frontalKeys) {
 | 
			
		||||
  // NOTE: Because we are in the Conditional Gaussian regime there are only
 | 
			
		||||
  // a few cases:
 | 
			
		||||
  // 1. continuous variable, make a Gaussian Mixture if there are hybrid
 | 
			
		||||
  // factors;
 | 
			
		||||
  // 1. continuous variable, make a hybrid Gaussian conditional if there are
 | 
			
		||||
  // hybrid factors;
 | 
			
		||||
  // 2. continuous variable, we make a Gaussian Factor if there are no hybrid
 | 
			
		||||
  // factors;
 | 
			
		||||
  // 3. discrete variable, no continuous factor is allowed
 | 
			
		||||
| 
						 | 
				
			
			@ -550,9 +550,10 @@ AlgebraicDecisionTree<Key> HybridGaussianFactorGraph::errorTree(
 | 
			
		|||
      f = hc->inner();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (auto gaussianMixture = dynamic_pointer_cast<HybridGaussianFactor>(f)) {
 | 
			
		||||
    if (auto hybridGaussianCond =
 | 
			
		||||
            dynamic_pointer_cast<HybridGaussianFactor>(f)) {
 | 
			
		||||
      // Compute factor error and add it.
 | 
			
		||||
      error_tree = error_tree + gaussianMixture->errorTree(continuousValues);
 | 
			
		||||
      error_tree = error_tree + hybridGaussianCond->errorTree(continuousValues);
 | 
			
		||||
    } else if (auto gaussian = dynamic_pointer_cast<GaussianFactor>(f)) {
 | 
			
		||||
      // If continuous only, get the (double) error
 | 
			
		||||
      // and add it to the error_tree
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -210,7 +210,7 @@ class GTSAM_EXPORT HybridGaussianFactorGraph
 | 
			
		|||
   * @brief Create a decision tree of factor graphs out of this hybrid factor
 | 
			
		||||
   * graph.
 | 
			
		||||
   *
 | 
			
		||||
   * For example, if there are two mixture factors, one with a discrete key A
 | 
			
		||||
   * For example, if there are two hybrid factors, one with a discrete key A
 | 
			
		||||
   * and one with a discrete key B, then the decision tree will have two levels,
 | 
			
		||||
   * one for A and one for B. The leaves of the tree will be the Gaussian
 | 
			
		||||
   * factors that have only continuous keys.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,11 +37,10 @@ namespace gtsam {
 | 
			
		|||
using NonlinearFactorValuePair = std::pair<NonlinearFactor::shared_ptr, double>;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Implementation of a discrete conditional mixture factor.
 | 
			
		||||
 * @brief Implementation of a discrete-conditioned hybrid factor.
 | 
			
		||||
 *
 | 
			
		||||
 * Implements a joint discrete-continuous factor where the discrete variable
 | 
			
		||||
 * serves to "select" a mixture component corresponding to a NonlinearFactor
 | 
			
		||||
 * type of measurement.
 | 
			
		||||
 * serves to "select" a hybrid component corresponding to a NonlinearFactor.
 | 
			
		||||
 *
 | 
			
		||||
 * This class stores all factors as HybridFactors which can then be typecast to
 | 
			
		||||
 * one of (NonlinearFactor, GaussianFactor) which can then be checked to perform
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -151,7 +151,7 @@ HybridGaussianFactorGraph::shared_ptr HybridNonlinearFactorGraph::linearize(
 | 
			
		|||
    if (!f) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    // Check if it is a nonlinear mixture factor
 | 
			
		||||
    // Check if it is a hybrid nonlinear factor
 | 
			
		||||
    if (auto mf = dynamic_pointer_cast<HybridNonlinearFactor>(f)) {
 | 
			
		||||
      const HybridGaussianFactor::shared_ptr& gmf =
 | 
			
		||||
          mf->linearize(continuousValues);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,7 +100,7 @@ HybridSmoother::addConditionals(const HybridGaussianFactorGraph &originalGraph,
 | 
			
		|||
  // If hybridBayesNet is not empty,
 | 
			
		||||
  // it means we have conditionals to add to the factor graph.
 | 
			
		||||
  if (!hybridBayesNet.empty()) {
 | 
			
		||||
    // We add all relevant conditional mixtures on the last continuous variable
 | 
			
		||||
    // We add all relevant hybrid conditionals on the last continuous variable
 | 
			
		||||
    // in the previous `hybridBayesNet` to the graph
 | 
			
		||||
 | 
			
		||||
    // Conditionals to remove from the bayes net
 | 
			
		||||
| 
						 | 
				
			
			@ -140,7 +140,7 @@ HybridSmoother::addConditionals(const HybridGaussianFactorGraph &originalGraph,
 | 
			
		|||
/* ************************************************************************* */
 | 
			
		||||
HybridGaussianConditional::shared_ptr HybridSmoother::gaussianMixture(
 | 
			
		||||
    size_t index) const {
 | 
			
		||||
  return hybridBayesNet_.at(index)->asMixture();
 | 
			
		||||
  return hybridBayesNet_.at(index)->asHybrid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ class GTSAM_EXPORT HybridSmoother {
 | 
			
		|||
   * Given new factors, perform an incremental update.
 | 
			
		||||
   * The relevant densities in the `hybridBayesNet` will be added to the input
 | 
			
		||||
   * graph (fragment), and then eliminated according to the `ordering`
 | 
			
		||||
   * presented. The remaining factor graph contains Gaussian mixture factors
 | 
			
		||||
   * presented. The remaining factor graph contains hybrid Gaussian factors
 | 
			
		||||
   * that are not connected to the variables in the ordering, or a single
 | 
			
		||||
   * discrete factor on all discrete keys, plus all discrete factors in the
 | 
			
		||||
   * original graph.
 | 
			
		||||
| 
						 | 
				
			
			@ -68,7 +68,13 @@ class GTSAM_EXPORT HybridSmoother {
 | 
			
		|||
      const HybridGaussianFactorGraph& graph,
 | 
			
		||||
      const HybridBayesNet& hybridBayesNet, const Ordering& ordering) const;
 | 
			
		||||
 | 
			
		||||
  /// Get the Gaussian Mixture from the Bayes Net posterior at `index`.
 | 
			
		||||
  /**
 | 
			
		||||
   * @brief Get the hybrid Gaussian conditional from
 | 
			
		||||
   * the Bayes Net posterior at `index`.
 | 
			
		||||
   *
 | 
			
		||||
   * @param index Indexing value.
 | 
			
		||||
   * @return HybridGaussianConditional::shared_ptr
 | 
			
		||||
   */
 | 
			
		||||
  HybridGaussianConditional::shared_ptr gaussianMixture(size_t index) const;
 | 
			
		||||
 | 
			
		||||
  /// Return the Bayes Net posterior.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,7 +65,7 @@ virtual class HybridConditional {
 | 
			
		|||
  double logProbability(const gtsam::HybridValues& values) const;
 | 
			
		||||
  double evaluate(const gtsam::HybridValues& values) const;
 | 
			
		||||
  double operator()(const gtsam::HybridValues& values) const;
 | 
			
		||||
  gtsam::HybridGaussianConditional* asMixture() const;
 | 
			
		||||
  gtsam::HybridGaussianConditional* asHybrid() const;
 | 
			
		||||
  gtsam::GaussianConditional* asGaussian() const;
 | 
			
		||||
  gtsam::DiscreteConditional* asDiscrete() const;
 | 
			
		||||
  gtsam::Factor* inner();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,7 +40,7 @@ inline HybridBayesNet createHybridBayesNet(size_t num_measurements = 1,
 | 
			
		|||
                                           bool manyModes = false) {
 | 
			
		||||
  HybridBayesNet bayesNet;
 | 
			
		||||
 | 
			
		||||
  // Create Gaussian mixture z_i = x0 + noise for each measurement.
 | 
			
		||||
  // Create hybrid Gaussian factor z_i = x0 + noise for each measurement.
 | 
			
		||||
  for (size_t i = 0; i < num_measurements; i++) {
 | 
			
		||||
    const auto mode_i = manyModes ? DiscreteKey{M(i), 2} : mode;
 | 
			
		||||
    std::vector<GaussianConditional::shared_ptr> conditionals{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -144,13 +144,13 @@ TEST(HybridBayesNet, Choose) {
 | 
			
		|||
 | 
			
		||||
  EXPECT_LONGS_EQUAL(4, gbn.size());
 | 
			
		||||
 | 
			
		||||
  EXPECT(assert_equal(*(*hybridBayesNet->at(0)->asMixture())(assignment),
 | 
			
		||||
  EXPECT(assert_equal(*(*hybridBayesNet->at(0)->asHybrid())(assignment),
 | 
			
		||||
                      *gbn.at(0)));
 | 
			
		||||
  EXPECT(assert_equal(*(*hybridBayesNet->at(1)->asMixture())(assignment),
 | 
			
		||||
  EXPECT(assert_equal(*(*hybridBayesNet->at(1)->asHybrid())(assignment),
 | 
			
		||||
                      *gbn.at(1)));
 | 
			
		||||
  EXPECT(assert_equal(*(*hybridBayesNet->at(2)->asMixture())(assignment),
 | 
			
		||||
  EXPECT(assert_equal(*(*hybridBayesNet->at(2)->asHybrid())(assignment),
 | 
			
		||||
                      *gbn.at(2)));
 | 
			
		||||
  EXPECT(assert_equal(*(*hybridBayesNet->at(3)->asMixture())(assignment),
 | 
			
		||||
  EXPECT(assert_equal(*(*hybridBayesNet->at(3)->asHybrid())(assignment),
 | 
			
		||||
                      *gbn.at(3)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -280,9 +280,9 @@ TEST(HybridBayesNet, Pruning) {
 | 
			
		|||
  const DiscreteValues discrete_values{{M(0), 1}, {M(1), 1}};
 | 
			
		||||
  const HybridValues hybridValues{delta.continuous(), discrete_values};
 | 
			
		||||
  double logProbability = 0;
 | 
			
		||||
  logProbability += posterior->at(0)->asMixture()->logProbability(hybridValues);
 | 
			
		||||
  logProbability += posterior->at(1)->asMixture()->logProbability(hybridValues);
 | 
			
		||||
  logProbability += posterior->at(2)->asMixture()->logProbability(hybridValues);
 | 
			
		||||
  logProbability += posterior->at(0)->asHybrid()->logProbability(hybridValues);
 | 
			
		||||
  logProbability += posterior->at(1)->asHybrid()->logProbability(hybridValues);
 | 
			
		||||
  logProbability += posterior->at(2)->asHybrid()->logProbability(hybridValues);
 | 
			
		||||
  // NOTE(dellaert): the discrete errors were not added in logProbability tree!
 | 
			
		||||
  logProbability +=
 | 
			
		||||
      posterior->at(3)->asDiscrete()->logProbability(hybridValues);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,8 +44,8 @@ TEST(HybridConditional, Invariants) {
 | 
			
		|||
  CHECK(hc0->isHybrid());
 | 
			
		||||
 | 
			
		||||
  // Check invariants as a HybridGaussianConditional.
 | 
			
		||||
  const auto mixture = hc0->asMixture();
 | 
			
		||||
  EXPECT(HybridGaussianConditional::CheckInvariants(*mixture, values));
 | 
			
		||||
  const auto conditional = hc0->asHybrid();
 | 
			
		||||
  EXPECT(HybridGaussianConditional::CheckInvariants(*conditional, values));
 | 
			
		||||
 | 
			
		||||
  // Check invariants as a HybridConditional.
 | 
			
		||||
  EXPECT(HybridConditional::CheckInvariants(*hc0, values));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -429,7 +429,7 @@ static HybridNonlinearFactorGraph createHybridNonlinearFactorGraph() {
 | 
			
		|||
  nfg.emplace_shared<PriorFactor<double>>(X(0), 0.0, noise_model);
 | 
			
		||||
  nfg.emplace_shared<PriorFactor<double>>(X(1), 1.0, noise_model);
 | 
			
		||||
 | 
			
		||||
  // Add mixture factor:
 | 
			
		||||
  // Add hybrid nonlinear factor:
 | 
			
		||||
  DiscreteKey m(M(0), 2);
 | 
			
		||||
  const auto zero_motion =
 | 
			
		||||
      std::make_shared<BetweenFactor<double>>(X(0), X(1), 0, noise_model);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ TEST(HybridFactorGraph, Keys) {
 | 
			
		|||
  // Add factor between x0 and x1
 | 
			
		||||
  hfg.add(JacobianFactor(X(0), I_3x3, X(1), -I_3x3, Z_3x1));
 | 
			
		||||
 | 
			
		||||
  // Add a gaussian mixture factor ϕ(x1, c1)
 | 
			
		||||
  // Add a hybrid Gaussian factor ϕ(x1, c1)
 | 
			
		||||
  DiscreteKey m1(M(1), 2);
 | 
			
		||||
  DecisionTree<Key, GaussianFactorValuePair> dt(
 | 
			
		||||
      M(1), {std::make_shared<JacobianFactor>(X(1), I_3x3, Z_3x1), 0.0},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,9 +52,8 @@ const std::vector<GaussianConditional::shared_ptr> conditionals{
 | 
			
		|||
                                             commonSigma),
 | 
			
		||||
    GaussianConditional::sharedMeanAndStddev(Z(0), I_1x1, X(0), Vector1(0.0),
 | 
			
		||||
                                             commonSigma)};
 | 
			
		||||
const HybridGaussianConditional mixture(
 | 
			
		||||
    {Z(0)}, {X(0)}, {mode},
 | 
			
		||||
    HybridGaussianConditional::Conditionals({mode}, conditionals));
 | 
			
		||||
const HybridGaussianConditional hybrid_conditional({Z(0)}, {X(0)}, mode,
 | 
			
		||||
                                                   conditionals);
 | 
			
		||||
}  // namespace equal_constants
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
| 
						 | 
				
			
			@ -62,21 +61,21 @@ const HybridGaussianConditional mixture(
 | 
			
		|||
TEST(HybridGaussianConditional, Invariants) {
 | 
			
		||||
  using namespace equal_constants;
 | 
			
		||||
 | 
			
		||||
  // Check that the mixture normalization constant is the max of all constants
 | 
			
		||||
  // which are all equal, in this case, hence:
 | 
			
		||||
  const double K = mixture.logNormalizationConstant();
 | 
			
		||||
  // Check that the conditional normalization constant is the max of all
 | 
			
		||||
  // constants which are all equal, in this case, hence:
 | 
			
		||||
  const double K = hybrid_conditional.logNormalizationConstant();
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(K, conditionals[0]->logNormalizationConstant(), 1e-8);
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(K, conditionals[1]->logNormalizationConstant(), 1e-8);
 | 
			
		||||
 | 
			
		||||
  EXPECT(HybridGaussianConditional::CheckInvariants(mixture, hv0));
 | 
			
		||||
  EXPECT(HybridGaussianConditional::CheckInvariants(mixture, hv1));
 | 
			
		||||
  EXPECT(HybridGaussianConditional::CheckInvariants(hybrid_conditional, hv0));
 | 
			
		||||
  EXPECT(HybridGaussianConditional::CheckInvariants(hybrid_conditional, hv1));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
/// Check LogProbability.
 | 
			
		||||
TEST(HybridGaussianConditional, LogProbability) {
 | 
			
		||||
  using namespace equal_constants;
 | 
			
		||||
  auto actual = mixture.logProbability(vv);
 | 
			
		||||
  auto actual = hybrid_conditional.logProbability(vv);
 | 
			
		||||
 | 
			
		||||
  // Check result.
 | 
			
		||||
  std::vector<DiscreteKey> discrete_keys = {mode};
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +89,7 @@ TEST(HybridGaussianConditional, LogProbability) {
 | 
			
		|||
  for (size_t mode : {0, 1}) {
 | 
			
		||||
    const HybridValues hv{vv, {{M(0), mode}}};
 | 
			
		||||
    EXPECT_DOUBLES_EQUAL(conditionals[mode]->logProbability(vv),
 | 
			
		||||
                         mixture.logProbability(hv), 1e-8);
 | 
			
		||||
                         hybrid_conditional.logProbability(hv), 1e-8);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +97,7 @@ TEST(HybridGaussianConditional, LogProbability) {
 | 
			
		|||
/// Check error.
 | 
			
		||||
TEST(HybridGaussianConditional, Error) {
 | 
			
		||||
  using namespace equal_constants;
 | 
			
		||||
  auto actual = mixture.errorTree(vv);
 | 
			
		||||
  auto actual = hybrid_conditional.errorTree(vv);
 | 
			
		||||
 | 
			
		||||
  // Check result.
 | 
			
		||||
  std::vector<DiscreteKey> discrete_keys = {mode};
 | 
			
		||||
| 
						 | 
				
			
			@ -111,8 +110,8 @@ TEST(HybridGaussianConditional, Error) {
 | 
			
		|||
  // Check for non-tree version.
 | 
			
		||||
  for (size_t mode : {0, 1}) {
 | 
			
		||||
    const HybridValues hv{vv, {{M(0), mode}}};
 | 
			
		||||
    EXPECT_DOUBLES_EQUAL(conditionals[mode]->error(vv), mixture.error(hv),
 | 
			
		||||
                         1e-8);
 | 
			
		||||
    EXPECT_DOUBLES_EQUAL(conditionals[mode]->error(vv),
 | 
			
		||||
                         hybrid_conditional.error(hv), 1e-8);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -123,11 +122,14 @@ TEST(HybridGaussianConditional, Likelihood) {
 | 
			
		|||
  using namespace equal_constants;
 | 
			
		||||
 | 
			
		||||
  // Compute likelihood
 | 
			
		||||
  auto likelihood = mixture.likelihood(vv);
 | 
			
		||||
  auto likelihood = hybrid_conditional.likelihood(vv);
 | 
			
		||||
 | 
			
		||||
  // Check that the mixture error and the likelihood error are the same.
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(mixture.error(hv0), likelihood->error(hv0), 1e-8);
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(mixture.error(hv1), likelihood->error(hv1), 1e-8);
 | 
			
		||||
  // Check that the hybrid conditional error and the likelihood error are the
 | 
			
		||||
  // same.
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(hybrid_conditional.error(hv0), likelihood->error(hv0),
 | 
			
		||||
                       1e-8);
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(hybrid_conditional.error(hv1), likelihood->error(hv1),
 | 
			
		||||
                       1e-8);
 | 
			
		||||
 | 
			
		||||
  // Check that likelihood error is as expected, i.e., just the errors of the
 | 
			
		||||
  // individual likelihoods, in the `equal_constants` case.
 | 
			
		||||
| 
						 | 
				
			
			@ -141,7 +143,8 @@ TEST(HybridGaussianConditional, Likelihood) {
 | 
			
		|||
  std::vector<double> ratio(2);
 | 
			
		||||
  for (size_t mode : {0, 1}) {
 | 
			
		||||
    const HybridValues hv{vv, {{M(0), mode}}};
 | 
			
		||||
    ratio[mode] = std::exp(-likelihood->error(hv)) / mixture.evaluate(hv);
 | 
			
		||||
    ratio[mode] =
 | 
			
		||||
        std::exp(-likelihood->error(hv)) / hybrid_conditional.evaluate(hv);
 | 
			
		||||
  }
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(ratio[0], ratio[1], 1e-8);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -155,16 +158,15 @@ const std::vector<GaussianConditional::shared_ptr> conditionals{
 | 
			
		|||
                                             0.5),
 | 
			
		||||
    GaussianConditional::sharedMeanAndStddev(Z(0), I_1x1, X(0), Vector1(0.0),
 | 
			
		||||
                                             3.0)};
 | 
			
		||||
const HybridGaussianConditional mixture(
 | 
			
		||||
    {Z(0)}, {X(0)}, {mode},
 | 
			
		||||
    HybridGaussianConditional::Conditionals({mode}, conditionals));
 | 
			
		||||
const HybridGaussianConditional hybrid_conditional({Z(0)}, {X(0)}, mode,
 | 
			
		||||
                                                   conditionals);
 | 
			
		||||
}  // namespace mode_dependent_constants
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
// Create a test for continuousParents.
 | 
			
		||||
TEST(HybridGaussianConditional, ContinuousParents) {
 | 
			
		||||
  using namespace mode_dependent_constants;
 | 
			
		||||
  const KeyVector continuousParentKeys = mixture.continuousParents();
 | 
			
		||||
  const KeyVector continuousParentKeys = hybrid_conditional.continuousParents();
 | 
			
		||||
  // Check that the continuous parent keys are correct:
 | 
			
		||||
  EXPECT(continuousParentKeys.size() == 1);
 | 
			
		||||
  EXPECT(continuousParentKeys[0] == X(0));
 | 
			
		||||
| 
						 | 
				
			
			@ -177,12 +179,14 @@ TEST(HybridGaussianConditional, Likelihood2) {
 | 
			
		|||
  using namespace mode_dependent_constants;
 | 
			
		||||
 | 
			
		||||
  // Compute likelihood
 | 
			
		||||
  auto likelihood = mixture.likelihood(vv);
 | 
			
		||||
  auto likelihood = hybrid_conditional.likelihood(vv);
 | 
			
		||||
 | 
			
		||||
  // Check that the mixture error and the likelihood error are as expected,
 | 
			
		||||
  // this invariant is the same as the equal noise case:
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(mixture.error(hv0), likelihood->error(hv0), 1e-8);
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(mixture.error(hv1), likelihood->error(hv1), 1e-8);
 | 
			
		||||
  // Check that the hybrid conditional error and the likelihood error are as
 | 
			
		||||
  // expected, this invariant is the same as the equal noise case:
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(hybrid_conditional.error(hv0), likelihood->error(hv0),
 | 
			
		||||
                       1e-8);
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(hybrid_conditional.error(hv1), likelihood->error(hv1),
 | 
			
		||||
                       1e-8);
 | 
			
		||||
 | 
			
		||||
  // Check the detailed JacobianFactor calculation for mode==1.
 | 
			
		||||
  {
 | 
			
		||||
| 
						 | 
				
			
			@ -195,7 +199,7 @@ TEST(HybridGaussianConditional, Likelihood2) {
 | 
			
		|||
    CHECK(jf1->rows() == 2);
 | 
			
		||||
 | 
			
		||||
    // Check that the constant C1 is properly encoded in the JacobianFactor.
 | 
			
		||||
    const double C1 = mixture.logNormalizationConstant() -
 | 
			
		||||
    const double C1 = hybrid_conditional.logNormalizationConstant() -
 | 
			
		||||
                      conditionals[1]->logNormalizationConstant();
 | 
			
		||||
    const double c1 = std::sqrt(2.0 * C1);
 | 
			
		||||
    Vector expected_unwhitened(2);
 | 
			
		||||
| 
						 | 
				
			
			@ -209,15 +213,16 @@ TEST(HybridGaussianConditional, Likelihood2) {
 | 
			
		|||
    Vector actual_whitened = jf1->error_vector(vv);
 | 
			
		||||
    EXPECT(assert_equal(expected_whitened, actual_whitened));
 | 
			
		||||
 | 
			
		||||
    // Check that the error is equal to the mixture error:
 | 
			
		||||
    EXPECT_DOUBLES_EQUAL(mixture.error(hv1), jf1->error(hv1), 1e-8);
 | 
			
		||||
    // Check that the error is equal to the conditional error:
 | 
			
		||||
    EXPECT_DOUBLES_EQUAL(hybrid_conditional.error(hv1), jf1->error(hv1), 1e-8);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Check that the ratio of probPrime to evaluate is the same for all modes.
 | 
			
		||||
  std::vector<double> ratio(2);
 | 
			
		||||
  for (size_t mode : {0, 1}) {
 | 
			
		||||
    const HybridValues hv{vv, {{M(0), mode}}};
 | 
			
		||||
    ratio[mode] = std::exp(-likelihood->error(hv)) / mixture.evaluate(hv);
 | 
			
		||||
    ratio[mode] =
 | 
			
		||||
        std::exp(-likelihood->error(hv)) / hybrid_conditional.evaluate(hv);
 | 
			
		||||
  }
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(ratio[0], ratio[1], 1e-8);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ using symbol_shorthand::X;
 | 
			
		|||
using symbol_shorthand::Z;
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
// Check iterators of empty mixture.
 | 
			
		||||
// Check iterators of empty hybrid factor.
 | 
			
		||||
TEST(HybridGaussianFactor, Constructor) {
 | 
			
		||||
  HybridGaussianFactor factor;
 | 
			
		||||
  HybridGaussianFactor::const_iterator const_it = factor.begin();
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +55,7 @@ TEST(HybridGaussianFactor, Constructor) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
// "Add" two mixture factors together.
 | 
			
		||||
// "Add" two hybrid factors together.
 | 
			
		||||
TEST(HybridGaussianFactor, Sum) {
 | 
			
		||||
  DiscreteKey m1(1, 2), m2(2, 3);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -78,20 +78,20 @@ TEST(HybridGaussianFactor, Sum) {
 | 
			
		|||
  // TODO(Frank): why specify keys at all? And: keys in factor should be *all*
 | 
			
		||||
  // keys, deviating from Kevin's scheme. Should we index DT on DiscreteKey?
 | 
			
		||||
  // Design review!
 | 
			
		||||
  HybridGaussianFactor mixtureFactorA({X(1), X(2)}, {m1}, factorsA);
 | 
			
		||||
  HybridGaussianFactor mixtureFactorB({X(1), X(3)}, {m2}, factorsB);
 | 
			
		||||
  HybridGaussianFactor hybridFactorA({X(1), X(2)}, {m1}, factorsA);
 | 
			
		||||
  HybridGaussianFactor hybridFactorB({X(1), X(3)}, {m2}, factorsB);
 | 
			
		||||
 | 
			
		||||
  // Check that number of keys is 3
 | 
			
		||||
  EXPECT_LONGS_EQUAL(3, mixtureFactorA.keys().size());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(3, hybridFactorA.keys().size());
 | 
			
		||||
 | 
			
		||||
  // Check that number of discrete keys is 1
 | 
			
		||||
  EXPECT_LONGS_EQUAL(1, mixtureFactorA.discreteKeys().size());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(1, hybridFactorA.discreteKeys().size());
 | 
			
		||||
 | 
			
		||||
  // Create sum of two mixture factors: it will be a decision tree now on both
 | 
			
		||||
  // Create sum of two hybrid factors: it will be a decision tree now on both
 | 
			
		||||
  // discrete variables m1 and m2:
 | 
			
		||||
  GaussianFactorGraphTree sum;
 | 
			
		||||
  sum += mixtureFactorA;
 | 
			
		||||
  sum += mixtureFactorB;
 | 
			
		||||
  sum += hybridFactorA;
 | 
			
		||||
  sum += hybridFactorB;
 | 
			
		||||
 | 
			
		||||
  // Let's check that this worked:
 | 
			
		||||
  Assignment<Key> mode;
 | 
			
		||||
| 
						 | 
				
			
			@ -112,7 +112,7 @@ TEST(HybridGaussianFactor, Printing) {
 | 
			
		|||
  auto f11 = std::make_shared<JacobianFactor>(X(1), A1, X(2), A2, b);
 | 
			
		||||
  std::vector<GaussianFactorValuePair> factors{{f10, 0.0}, {f11, 0.0}};
 | 
			
		||||
 | 
			
		||||
  HybridGaussianFactor mixtureFactor({X(1), X(2)}, {m1}, factors);
 | 
			
		||||
  HybridGaussianFactor hybridFactor({X(1), X(2)}, {m1}, factors);
 | 
			
		||||
 | 
			
		||||
  std::string expected =
 | 
			
		||||
      R"(HybridGaussianFactor
 | 
			
		||||
| 
						 | 
				
			
			@ -144,7 +144,7 @@ Hybrid [x1 x2; 1]{
 | 
			
		|||
 | 
			
		||||
}
 | 
			
		||||
)";
 | 
			
		||||
  EXPECT(assert_print_equal(expected, mixtureFactor));
 | 
			
		||||
  EXPECT(assert_print_equal(expected, hybridFactor));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
| 
						 | 
				
			
			@ -181,7 +181,7 @@ TEST(HybridGaussianFactor, Error) {
 | 
			
		|||
  auto f1 = std::make_shared<JacobianFactor>(X(1), A11, X(2), A12, b);
 | 
			
		||||
  std::vector<GaussianFactorValuePair> factors{{f0, 0.0}, {f1, 0.0}};
 | 
			
		||||
 | 
			
		||||
  HybridGaussianFactor mixtureFactor({X(1), X(2)}, {m1}, factors);
 | 
			
		||||
  HybridGaussianFactor hybridFactor({X(1), X(2)}, {m1}, factors);
 | 
			
		||||
 | 
			
		||||
  VectorValues continuousValues;
 | 
			
		||||
  continuousValues.insert(X(1), Vector2(0, 0));
 | 
			
		||||
| 
						 | 
				
			
			@ -189,7 +189,7 @@ TEST(HybridGaussianFactor, Error) {
 | 
			
		|||
 | 
			
		||||
  // error should return a tree of errors, with nodes for each discrete value.
 | 
			
		||||
  AlgebraicDecisionTree<Key> error_tree =
 | 
			
		||||
      mixtureFactor.errorTree(continuousValues);
 | 
			
		||||
      hybridFactor.errorTree(continuousValues);
 | 
			
		||||
 | 
			
		||||
  std::vector<DiscreteKey> discrete_keys = {m1};
 | 
			
		||||
  // Error values for regression test
 | 
			
		||||
| 
						 | 
				
			
			@ -202,7 +202,7 @@ TEST(HybridGaussianFactor, Error) {
 | 
			
		|||
  DiscreteValues discreteValues;
 | 
			
		||||
  discreteValues[m1.first] = 1;
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(
 | 
			
		||||
      4.0, mixtureFactor.error({continuousValues, discreteValues}), 1e-9);
 | 
			
		||||
      4.0, hybridFactor.error({continuousValues, discreteValues}), 1e-9);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace test_gmm {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,8 +69,8 @@ TEST(HybridGaussianFactorGraph, Creation) {
 | 
			
		|||
 | 
			
		||||
  hfg.emplace_shared<JacobianFactor>(X(0), I_3x3, Z_3x1);
 | 
			
		||||
 | 
			
		||||
  // Define a gaussian mixture conditional P(x0|x1, c0) and add it to the factor
 | 
			
		||||
  // graph
 | 
			
		||||
  // Define a hybrid gaussian conditional P(x0|x1, c0)
 | 
			
		||||
  // and add it to the factor graph.
 | 
			
		||||
  HybridGaussianConditional gm(
 | 
			
		||||
      {X(0)}, {X(1)}, DiscreteKeys(DiscreteKey{M(0), 2}),
 | 
			
		||||
      HybridGaussianConditional::Conditionals(
 | 
			
		||||
| 
						 | 
				
			
			@ -125,7 +125,7 @@ TEST(HybridGaussianFactorGraph, eliminateFullSequentialEqualChance) {
 | 
			
		|||
  // Add factor between x0 and x1
 | 
			
		||||
  hfg.add(JacobianFactor(X(0), I_3x3, X(1), -I_3x3, Z_3x1));
 | 
			
		||||
 | 
			
		||||
  // Add a gaussian mixture factor ϕ(x1, c1)
 | 
			
		||||
  // Add a hybrid gaussian factor ϕ(x1, c1)
 | 
			
		||||
  DiscreteKey m1(M(1), 2);
 | 
			
		||||
  DecisionTree<Key, GaussianFactorValuePair> dt(
 | 
			
		||||
      M(1), {std::make_shared<JacobianFactor>(X(1), I_3x3, Z_3x1), 0.0},
 | 
			
		||||
| 
						 | 
				
			
			@ -720,9 +720,9 @@ TEST(HybridGaussianFactorGraph, assembleGraphTree) {
 | 
			
		|||
 | 
			
		||||
  // Create expected decision tree with two factor graphs:
 | 
			
		||||
 | 
			
		||||
  // Get mixture factor:
 | 
			
		||||
  auto mixture = fg.at<HybridGaussianFactor>(0);
 | 
			
		||||
  CHECK(mixture);
 | 
			
		||||
  // Get hybrid factor:
 | 
			
		||||
  auto hybrid = fg.at<HybridGaussianFactor>(0);
 | 
			
		||||
  CHECK(hybrid);
 | 
			
		||||
 | 
			
		||||
  // Get prior factor:
 | 
			
		||||
  const auto gf = fg.at<HybridConditional>(1);
 | 
			
		||||
| 
						 | 
				
			
			@ -737,8 +737,8 @@ TEST(HybridGaussianFactorGraph, assembleGraphTree) {
 | 
			
		|||
  // Expected decision tree with two factor graphs:
 | 
			
		||||
  // f(x0;mode=0)P(x0) and f(x0;mode=1)P(x0)
 | 
			
		||||
  GaussianFactorGraphTree expected{
 | 
			
		||||
      M(0), GaussianFactorGraph(std::vector<GF>{(*mixture)(d0), prior}),
 | 
			
		||||
      GaussianFactorGraph(std::vector<GF>{(*mixture)(d1), prior})};
 | 
			
		||||
      M(0), GaussianFactorGraph(std::vector<GF>{(*hybrid)(d0), prior}),
 | 
			
		||||
      GaussianFactorGraph(std::vector<GF>{(*hybrid)(d1), prior})};
 | 
			
		||||
 | 
			
		||||
  EXPECT(assert_equal(expected(d0), actual(d0), 1e-5));
 | 
			
		||||
  EXPECT(assert_equal(expected(d1), actual(d1), 1e-5));
 | 
			
		||||
| 
						 | 
				
			
			@ -802,7 +802,7 @@ TEST(HybridGaussianFactorGraph, EliminateTiny1) {
 | 
			
		|||
  // Create expected Bayes Net:
 | 
			
		||||
  HybridBayesNet expectedBayesNet;
 | 
			
		||||
 | 
			
		||||
  // Create Gaussian mixture on X(0).
 | 
			
		||||
  // Create hybrid Gaussian factor on X(0).
 | 
			
		||||
  using tiny::mode;
 | 
			
		||||
  // regression, but mean checked to be 5.0 in both cases:
 | 
			
		||||
  const auto conditional0 = std::make_shared<GaussianConditional>(
 | 
			
		||||
| 
						 | 
				
			
			@ -835,7 +835,7 @@ TEST(HybridGaussianFactorGraph, EliminateTiny1Swapped) {
 | 
			
		|||
  const DiscreteKey mode{M(0), 2};
 | 
			
		||||
  HybridBayesNet bn;
 | 
			
		||||
 | 
			
		||||
  // Create Gaussian mixture z_0 = x0 + noise for each measurement.
 | 
			
		||||
  // Create hybrid Gaussian factor z_0 = x0 + noise for each measurement.
 | 
			
		||||
  std::vector<GaussianConditional::shared_ptr> conditionals{
 | 
			
		||||
      GaussianConditional::sharedMeanAndStddev(Z(0), I_1x1, X(0), Z_1x1, 3),
 | 
			
		||||
      GaussianConditional::sharedMeanAndStddev(Z(0), I_1x1, X(0), Z_1x1, 0.5)};
 | 
			
		||||
| 
						 | 
				
			
			@ -863,7 +863,7 @@ TEST(HybridGaussianFactorGraph, EliminateTiny1Swapped) {
 | 
			
		|||
  // Create expected Bayes Net:
 | 
			
		||||
  HybridBayesNet expectedBayesNet;
 | 
			
		||||
 | 
			
		||||
  // Create Gaussian mixture on X(0).
 | 
			
		||||
  // Create hybrid Gaussian factor on X(0).
 | 
			
		||||
  // regression, but mean checked to be 5.0 in both cases:
 | 
			
		||||
  const auto conditional0 = std::make_shared<GaussianConditional>(
 | 
			
		||||
                 X(0), Vector1(10.1379), I_1x1 * 2.02759),
 | 
			
		||||
| 
						 | 
				
			
			@ -900,7 +900,7 @@ TEST(HybridGaussianFactorGraph, EliminateTiny2) {
 | 
			
		|||
  // Create expected Bayes Net:
 | 
			
		||||
  HybridBayesNet expectedBayesNet;
 | 
			
		||||
 | 
			
		||||
  // Create Gaussian mixture on X(0).
 | 
			
		||||
  // Create hybrid Gaussian factor on X(0).
 | 
			
		||||
  using tiny::mode;
 | 
			
		||||
  // regression, but mean checked to be 5.0 in both cases:
 | 
			
		||||
  const auto conditional0 = std::make_shared<GaussianConditional>(
 | 
			
		||||
| 
						 | 
				
			
			@ -953,7 +953,7 @@ TEST(HybridGaussianFactorGraph, EliminateSwitchingNetwork) {
 | 
			
		|||
 | 
			
		||||
  // Add measurements:
 | 
			
		||||
  for (size_t t : {0, 1, 2}) {
 | 
			
		||||
    // Create Gaussian mixture on Z(t) conditioned on X(t) and mode N(t):
 | 
			
		||||
    // Create hybrid Gaussian factor on Z(t) conditioned on X(t) and mode N(t):
 | 
			
		||||
    const auto noise_mode_t = DiscreteKey{N(t), 2};
 | 
			
		||||
    std::vector<GaussianConditional::shared_ptr> conditionals{
 | 
			
		||||
        GaussianConditional::sharedMeanAndStddev(Z(t), I_1x1, X(t), Z_1x1, 0.5),
 | 
			
		||||
| 
						 | 
				
			
			@ -970,7 +970,8 @@ TEST(HybridGaussianFactorGraph, EliminateSwitchingNetwork) {
 | 
			
		|||
 | 
			
		||||
  // Add motion models:
 | 
			
		||||
  for (size_t t : {2, 1}) {
 | 
			
		||||
    // Create Gaussian mixture on X(t) conditioned on X(t-1) and mode M(t-1):
 | 
			
		||||
    // Create hybrid Gaussian factor on X(t) conditioned on X(t-1)
 | 
			
		||||
    // and mode M(t-1):
 | 
			
		||||
    const auto motion_model_t = DiscreteKey{M(t), 2};
 | 
			
		||||
    std::vector<GaussianConditional::shared_ptr> conditionals{
 | 
			
		||||
        GaussianConditional::sharedMeanAndStddev(X(t), I_1x1, X(t - 1), Z_1x1,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -333,13 +333,13 @@ TEST(HybridGaussianElimination, Incremental_approximate) {
 | 
			
		|||
  // each with 2, 4, 8, and 5 (pruned) leaves respetively.
 | 
			
		||||
  EXPECT_LONGS_EQUAL(4, incrementalHybrid.size());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(
 | 
			
		||||
      2, incrementalHybrid[X(0)]->conditional()->asMixture()->nrComponents());
 | 
			
		||||
      2, incrementalHybrid[X(0)]->conditional()->asHybrid()->nrComponents());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(
 | 
			
		||||
      3, incrementalHybrid[X(1)]->conditional()->asMixture()->nrComponents());
 | 
			
		||||
      3, incrementalHybrid[X(1)]->conditional()->asHybrid()->nrComponents());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(
 | 
			
		||||
      5, incrementalHybrid[X(2)]->conditional()->asMixture()->nrComponents());
 | 
			
		||||
      5, incrementalHybrid[X(2)]->conditional()->asHybrid()->nrComponents());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(
 | 
			
		||||
      5, incrementalHybrid[X(3)]->conditional()->asMixture()->nrComponents());
 | 
			
		||||
      5, incrementalHybrid[X(3)]->conditional()->asHybrid()->nrComponents());
 | 
			
		||||
 | 
			
		||||
  /***** Run Round 2 *****/
 | 
			
		||||
  HybridGaussianFactorGraph graph2;
 | 
			
		||||
| 
						 | 
				
			
			@ -354,9 +354,9 @@ TEST(HybridGaussianElimination, Incremental_approximate) {
 | 
			
		|||
  // with 5 (pruned) leaves.
 | 
			
		||||
  CHECK_EQUAL(5, incrementalHybrid.size());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(
 | 
			
		||||
      5, incrementalHybrid[X(3)]->conditional()->asMixture()->nrComponents());
 | 
			
		||||
      5, incrementalHybrid[X(3)]->conditional()->asHybrid()->nrComponents());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(
 | 
			
		||||
      5, incrementalHybrid[X(4)]->conditional()->asMixture()->nrComponents());
 | 
			
		||||
      5, incrementalHybrid[X(4)]->conditional()->asHybrid()->nrComponents());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************/
 | 
			
		||||
| 
						 | 
				
			
			@ -422,9 +422,8 @@ TEST(HybridGaussianISAM, NonTrivial) {
 | 
			
		|||
                                                    noise_model);
 | 
			
		||||
  std::vector<std::pair<PlanarMotionModel::shared_ptr, double>> components = {
 | 
			
		||||
      {moving, 0.0}, {still, 0.0}};
 | 
			
		||||
  auto mixtureFactor = std::make_shared<HybridNonlinearFactor>(
 | 
			
		||||
  fg.emplace_shared<HybridNonlinearFactor>(
 | 
			
		||||
      contKeys, gtsam::DiscreteKey(M(1), 2), components);
 | 
			
		||||
  fg.push_back(mixtureFactor);
 | 
			
		||||
 | 
			
		||||
  // Add equivalent of ImuFactor
 | 
			
		||||
  fg.emplace_shared<BetweenFactor<Pose2>>(X(0), X(1), Pose2(1.0, 0.0, 0),
 | 
			
		||||
| 
						 | 
				
			
			@ -462,9 +461,8 @@ TEST(HybridGaussianISAM, NonTrivial) {
 | 
			
		|||
  moving =
 | 
			
		||||
      std::make_shared<PlanarMotionModel>(W(1), W(2), odometry, noise_model);
 | 
			
		||||
  components = {{moving, 0.0}, {still, 0.0}};
 | 
			
		||||
  mixtureFactor = std::make_shared<HybridNonlinearFactor>(
 | 
			
		||||
  fg.emplace_shared<HybridNonlinearFactor>(
 | 
			
		||||
      contKeys, gtsam::DiscreteKey(M(2), 2), components);
 | 
			
		||||
  fg.push_back(mixtureFactor);
 | 
			
		||||
 | 
			
		||||
  // Add equivalent of ImuFactor
 | 
			
		||||
  fg.emplace_shared<BetweenFactor<Pose2>>(X(1), X(2), Pose2(1.0, 0.0, 0),
 | 
			
		||||
| 
						 | 
				
			
			@ -505,9 +503,8 @@ TEST(HybridGaussianISAM, NonTrivial) {
 | 
			
		|||
  moving =
 | 
			
		||||
      std::make_shared<PlanarMotionModel>(W(2), W(3), odometry, noise_model);
 | 
			
		||||
  components = {{moving, 0.0}, {still, 0.0}};
 | 
			
		||||
  mixtureFactor = std::make_shared<HybridNonlinearFactor>(
 | 
			
		||||
  fg.emplace_shared<HybridNonlinearFactor>(
 | 
			
		||||
      contKeys, gtsam::DiscreteKey(M(3), 2), components);
 | 
			
		||||
  fg.push_back(mixtureFactor);
 | 
			
		||||
 | 
			
		||||
  // Add equivalent of ImuFactor
 | 
			
		||||
  fg.emplace_shared<BetweenFactor<Pose2>>(X(2), X(3), Pose2(1.0, 0.0, 0),
 | 
			
		||||
| 
						 | 
				
			
			@ -551,7 +548,7 @@ TEST(HybridGaussianISAM, NonTrivial) {
 | 
			
		|||
 | 
			
		||||
  // Test if pruning worked correctly by checking that we only have 3 leaves in
 | 
			
		||||
  // the last node.
 | 
			
		||||
  auto lastConditional = inc[X(3)]->conditional()->asMixture();
 | 
			
		||||
  auto lastConditional = inc[X(3)]->conditional()->asHybrid();
 | 
			
		||||
  EXPECT_LONGS_EQUAL(3, lastConditional->nrComponents());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,7 +35,7 @@ using symbol_shorthand::M;
 | 
			
		|||
using symbol_shorthand::X;
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
// Check iterators of empty mixture.
 | 
			
		||||
// Check iterators of empty hybrid factor.
 | 
			
		||||
TEST(HybridNonlinearFactor, Constructor) {
 | 
			
		||||
  HybridNonlinearFactor factor;
 | 
			
		||||
  HybridNonlinearFactor::const_iterator const_it = factor.begin();
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +60,7 @@ TEST(HybridNonlinearFactor, Printing) {
 | 
			
		|||
      std::make_shared<BetweenFactor<double>>(X(1), X(2), between1, model);
 | 
			
		||||
  std::vector<NonlinearFactorValuePair> factors{{f0, 0.0}, {f1, 0.0}};
 | 
			
		||||
 | 
			
		||||
  HybridNonlinearFactor mixtureFactor({X(1), X(2)}, {m1}, factors);
 | 
			
		||||
  HybridNonlinearFactor hybridFactor({X(1), X(2)}, {m1}, factors);
 | 
			
		||||
 | 
			
		||||
  std::string expected =
 | 
			
		||||
      R"(Hybrid [x1 x2; 1]
 | 
			
		||||
| 
						 | 
				
			
			@ -69,7 +69,7 @@ HybridNonlinearFactor
 | 
			
		|||
 0 Leaf Nonlinear factor on 2 keys
 | 
			
		||||
 1 Leaf Nonlinear factor on 2 keys
 | 
			
		||||
)";
 | 
			
		||||
  EXPECT(assert_print_equal(expected, mixtureFactor));
 | 
			
		||||
  EXPECT(assert_print_equal(expected, hybridFactor));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
| 
						 | 
				
			
			@ -94,14 +94,14 @@ static HybridNonlinearFactor getHybridNonlinearFactor() {
 | 
			
		|||
/* ************************************************************************* */
 | 
			
		||||
// Test the error of the HybridNonlinearFactor
 | 
			
		||||
TEST(HybridNonlinearFactor, Error) {
 | 
			
		||||
  auto mixtureFactor = getHybridNonlinearFactor();
 | 
			
		||||
  auto hybridFactor = getHybridNonlinearFactor();
 | 
			
		||||
 | 
			
		||||
  Values continuousValues;
 | 
			
		||||
  continuousValues.insert<double>(X(1), 0);
 | 
			
		||||
  continuousValues.insert<double>(X(2), 1);
 | 
			
		||||
 | 
			
		||||
  AlgebraicDecisionTree<Key> error_tree =
 | 
			
		||||
      mixtureFactor.errorTree(continuousValues);
 | 
			
		||||
      hybridFactor.errorTree(continuousValues);
 | 
			
		||||
 | 
			
		||||
  DiscreteKey m1(1, 2);
 | 
			
		||||
  std::vector<DiscreteKey> discrete_keys = {m1};
 | 
			
		||||
| 
						 | 
				
			
			@ -114,8 +114,8 @@ TEST(HybridNonlinearFactor, Error) {
 | 
			
		|||
/* ************************************************************************* */
 | 
			
		||||
// Test dim of the HybridNonlinearFactor
 | 
			
		||||
TEST(HybridNonlinearFactor, Dim) {
 | 
			
		||||
  auto mixtureFactor = getHybridNonlinearFactor();
 | 
			
		||||
  EXPECT_LONGS_EQUAL(1, mixtureFactor.dim());
 | 
			
		||||
  auto hybridFactor = getHybridNonlinearFactor();
 | 
			
		||||
  EXPECT_LONGS_EQUAL(1, hybridFactor.dim());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -281,7 +281,7 @@ TEST(HybridFactorGraph, EliminationTree) {
 | 
			
		|||
TEST(GaussianElimination, Eliminate_x0) {
 | 
			
		||||
  Switching self(3);
 | 
			
		||||
 | 
			
		||||
  // Gather factors on x1, has a simple Gaussian and a mixture factor.
 | 
			
		||||
  // Gather factors on x1, has a simple Gaussian and a hybrid factor.
 | 
			
		||||
  HybridGaussianFactorGraph factors;
 | 
			
		||||
  // Add gaussian prior
 | 
			
		||||
  factors.push_back(self.linearizedFactorGraph[0]);
 | 
			
		||||
| 
						 | 
				
			
			@ -306,7 +306,7 @@ TEST(GaussianElimination, Eliminate_x0) {
 | 
			
		|||
TEST(HybridsGaussianElimination, Eliminate_x1) {
 | 
			
		||||
  Switching self(3);
 | 
			
		||||
 | 
			
		||||
  // Gather factors on x1, will be two mixture factors (with x0 and x2, resp.).
 | 
			
		||||
  // Gather factors on x1, will be two hybrid factors (with x0 and x2, resp.).
 | 
			
		||||
  HybridGaussianFactorGraph factors;
 | 
			
		||||
  factors.push_back(self.linearizedFactorGraph[1]);  // involves m0
 | 
			
		||||
  factors.push_back(self.linearizedFactorGraph[2]);  // involves m1
 | 
			
		||||
| 
						 | 
				
			
			@ -349,18 +349,18 @@ TEST(HybridGaussianElimination, EliminateHybrid_2_Variable) {
 | 
			
		|||
  // Eliminate x0
 | 
			
		||||
  const Ordering ordering{X(0), X(1)};
 | 
			
		||||
 | 
			
		||||
  const auto [hybridConditionalMixture, factorOnModes] =
 | 
			
		||||
  const auto [hybridConditional, factorOnModes] =
 | 
			
		||||
      EliminateHybrid(factors, ordering);
 | 
			
		||||
 | 
			
		||||
  auto gaussianConditionalMixture =
 | 
			
		||||
  auto hybridGaussianConditional =
 | 
			
		||||
      dynamic_pointer_cast<HybridGaussianConditional>(
 | 
			
		||||
          hybridConditionalMixture->inner());
 | 
			
		||||
          hybridConditional->inner());
 | 
			
		||||
 | 
			
		||||
  CHECK(gaussianConditionalMixture);
 | 
			
		||||
  CHECK(hybridGaussianConditional);
 | 
			
		||||
  // Frontals = [x0, x1]
 | 
			
		||||
  EXPECT_LONGS_EQUAL(2, gaussianConditionalMixture->nrFrontals());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(2, hybridGaussianConditional->nrFrontals());
 | 
			
		||||
  // 1 parent, which is the mode
 | 
			
		||||
  EXPECT_LONGS_EQUAL(1, gaussianConditionalMixture->nrParents());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(1, hybridGaussianConditional->nrParents());
 | 
			
		||||
 | 
			
		||||
  // This is now a discreteFactor
 | 
			
		||||
  auto discreteFactor = dynamic_pointer_cast<DecisionTreeFactor>(factorOnModes);
 | 
			
		||||
| 
						 | 
				
			
			@ -849,8 +849,8 @@ namespace test_relinearization {
 | 
			
		|||
 * This way we can directly provide the likelihoods and
 | 
			
		||||
 * then perform (re-)linearization.
 | 
			
		||||
 *
 | 
			
		||||
 * @param means The means of the GaussianMixtureFactor components.
 | 
			
		||||
 * @param sigmas The covariances of the GaussianMixtureFactor components.
 | 
			
		||||
 * @param means The means of the HybridGaussianFactor components.
 | 
			
		||||
 * @param sigmas The covariances of the HybridGaussianFactor components.
 | 
			
		||||
 * @param m1 The discrete key.
 | 
			
		||||
 * @param x0_measurement A measurement on X0
 | 
			
		||||
 * @return HybridGaussianFactorGraph
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -358,13 +358,13 @@ TEST(HybridNonlinearISAM, Incremental_approximate) {
 | 
			
		|||
  // each with 2, 4, 8, and 5 (pruned) leaves respetively.
 | 
			
		||||
  EXPECT_LONGS_EQUAL(4, bayesTree.size());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(
 | 
			
		||||
      2, bayesTree[X(0)]->conditional()->asMixture()->nrComponents());
 | 
			
		||||
      2, bayesTree[X(0)]->conditional()->asHybrid()->nrComponents());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(
 | 
			
		||||
      3, bayesTree[X(1)]->conditional()->asMixture()->nrComponents());
 | 
			
		||||
      3, bayesTree[X(1)]->conditional()->asHybrid()->nrComponents());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(
 | 
			
		||||
      5, bayesTree[X(2)]->conditional()->asMixture()->nrComponents());
 | 
			
		||||
      5, bayesTree[X(2)]->conditional()->asHybrid()->nrComponents());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(
 | 
			
		||||
      5, bayesTree[X(3)]->conditional()->asMixture()->nrComponents());
 | 
			
		||||
      5, bayesTree[X(3)]->conditional()->asHybrid()->nrComponents());
 | 
			
		||||
 | 
			
		||||
  /***** Run Round 2 *****/
 | 
			
		||||
  HybridGaussianFactorGraph graph2;
 | 
			
		||||
| 
						 | 
				
			
			@ -382,9 +382,9 @@ TEST(HybridNonlinearISAM, Incremental_approximate) {
 | 
			
		|||
  // with 5 (pruned) leaves.
 | 
			
		||||
  CHECK_EQUAL(5, bayesTree.size());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(
 | 
			
		||||
      5, bayesTree[X(3)]->conditional()->asMixture()->nrComponents());
 | 
			
		||||
      5, bayesTree[X(3)]->conditional()->asHybrid()->nrComponents());
 | 
			
		||||
  EXPECT_LONGS_EQUAL(
 | 
			
		||||
      5, bayesTree[X(4)]->conditional()->asMixture()->nrComponents());
 | 
			
		||||
      5, bayesTree[X(4)]->conditional()->asHybrid()->nrComponents());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************/
 | 
			
		||||
| 
						 | 
				
			
			@ -441,9 +441,8 @@ TEST(HybridNonlinearISAM, NonTrivial) {
 | 
			
		|||
                                                    noise_model);
 | 
			
		||||
  std::vector<std::pair<PlanarMotionModel::shared_ptr, double>> components = {
 | 
			
		||||
      {moving, 0.0}, {still, 0.0}};
 | 
			
		||||
  auto mixtureFactor = std::make_shared<HybridNonlinearFactor>(
 | 
			
		||||
  fg.emplace_shared<HybridNonlinearFactor>(
 | 
			
		||||
      contKeys, gtsam::DiscreteKey(M(1), 2), components);
 | 
			
		||||
  fg.push_back(mixtureFactor);
 | 
			
		||||
 | 
			
		||||
  // Add equivalent of ImuFactor
 | 
			
		||||
  fg.emplace_shared<BetweenFactor<Pose2>>(X(0), X(1), Pose2(1.0, 0.0, 0),
 | 
			
		||||
| 
						 | 
				
			
			@ -481,9 +480,8 @@ TEST(HybridNonlinearISAM, NonTrivial) {
 | 
			
		|||
  moving =
 | 
			
		||||
      std::make_shared<PlanarMotionModel>(W(1), W(2), odometry, noise_model);
 | 
			
		||||
  components = {{moving, 0.0}, {still, 0.0}};
 | 
			
		||||
  mixtureFactor = std::make_shared<HybridNonlinearFactor>(
 | 
			
		||||
  fg.emplace_shared<HybridNonlinearFactor>(
 | 
			
		||||
      contKeys, gtsam::DiscreteKey(M(2), 2), components);
 | 
			
		||||
  fg.push_back(mixtureFactor);
 | 
			
		||||
 | 
			
		||||
  // Add equivalent of ImuFactor
 | 
			
		||||
  fg.emplace_shared<BetweenFactor<Pose2>>(X(1), X(2), Pose2(1.0, 0.0, 0),
 | 
			
		||||
| 
						 | 
				
			
			@ -524,9 +522,8 @@ TEST(HybridNonlinearISAM, NonTrivial) {
 | 
			
		|||
  moving =
 | 
			
		||||
      std::make_shared<PlanarMotionModel>(W(2), W(3), odometry, noise_model);
 | 
			
		||||
  components = {{moving, 0.0}, {still, 0.0}};
 | 
			
		||||
  mixtureFactor = std::make_shared<HybridNonlinearFactor>(
 | 
			
		||||
  fg.emplace_shared<HybridNonlinearFactor>(
 | 
			
		||||
      contKeys, gtsam::DiscreteKey(M(3), 2), components);
 | 
			
		||||
  fg.push_back(mixtureFactor);
 | 
			
		||||
 | 
			
		||||
  // Add equivalent of ImuFactor
 | 
			
		||||
  fg.emplace_shared<BetweenFactor<Pose2>>(X(2), X(3), Pose2(1.0, 0.0, 0),
 | 
			
		||||
| 
						 | 
				
			
			@ -572,7 +569,7 @@ TEST(HybridNonlinearISAM, NonTrivial) {
 | 
			
		|||
 | 
			
		||||
  // Test if pruning worked correctly by checking that
 | 
			
		||||
  // we only have 3 leaves in the last node.
 | 
			
		||||
  auto lastConditional = bayesTree[X(3)]->conditional()->asMixture();
 | 
			
		||||
  auto lastConditional = bayesTree[X(3)]->conditional()->asHybrid();
 | 
			
		||||
  EXPECT_LONGS_EQUAL(3, lastConditional->nrComponents());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,9 +46,9 @@ class TestHybridGaussianFactorGraph(GtsamTestCase):
 | 
			
		|||
 | 
			
		||||
        self.assertEqual(hbn.size(), 2)
 | 
			
		||||
 | 
			
		||||
        mixture = hbn.at(0).inner()
 | 
			
		||||
        self.assertIsInstance(mixture, HybridGaussianConditional)
 | 
			
		||||
        self.assertEqual(len(mixture.keys()), 2)
 | 
			
		||||
        hybridCond = hbn.at(0).inner()
 | 
			
		||||
        self.assertIsInstance(hybridCond, HybridGaussianConditional)
 | 
			
		||||
        self.assertEqual(len(hybridCond.keys()), 2)
 | 
			
		||||
 | 
			
		||||
        discrete_conditional = hbn.at(hbn.size() - 1).inner()
 | 
			
		||||
        self.assertIsInstance(discrete_conditional, DiscreteConditional)
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +90,7 @@ class TestHybridGaussianFactorGraph(GtsamTestCase):
 | 
			
		|||
        # Create mode key: 0 is low-noise, 1 is high-noise.
 | 
			
		||||
        mode = (M(0), 2)
 | 
			
		||||
 | 
			
		||||
        # Create Gaussian mixture Z(0) = X(0) + noise for each measurement.
 | 
			
		||||
        # Create hybrid Gaussian conditional Z(0) = X(0) + noise for each measurement.
 | 
			
		||||
        I_1x1 = np.eye(1)
 | 
			
		||||
        for i in range(num_measurements):
 | 
			
		||||
            conditional0 = GaussianConditional.FromMeanAndStddev(Z(i),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue