No more hiding!
parent
586b177b78
commit
518ea81d1e
|
@ -31,45 +31,6 @@
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
/* *******************************************************************************/
|
|
||||||
HybridGaussianFactor::FactorValuePairs HybridGaussianFactor::augment(
|
|
||||||
const FactorValuePairs& factors) {
|
|
||||||
// Find the minimum value so we can "proselytize" to positive values.
|
|
||||||
// Done because we can't have sqrt of negative numbers.
|
|
||||||
DecisionTree<Key, GaussianFactor::shared_ptr> gaussianFactors;
|
|
||||||
AlgebraicDecisionTree<Key> valueTree;
|
|
||||||
std::tie(gaussianFactors, valueTree) = unzip(factors);
|
|
||||||
|
|
||||||
// Compute minimum value for normalization.
|
|
||||||
double min_value = valueTree.min();
|
|
||||||
|
|
||||||
// Finally, update the [A|b] matrices.
|
|
||||||
auto update = [&min_value](const auto& gfv) -> GaussianFactorValuePair {
|
|
||||||
auto [gf, value] = gfv;
|
|
||||||
|
|
||||||
auto jf = std::dynamic_pointer_cast<JacobianFactor>(gf);
|
|
||||||
if (!jf) return {gf, 0.0}; // should this be zero or infinite?
|
|
||||||
|
|
||||||
double normalized_value = value - min_value;
|
|
||||||
|
|
||||||
// If the value is 0, do nothing
|
|
||||||
if (normalized_value == 0.0) return {gf, value};
|
|
||||||
|
|
||||||
GaussianFactorGraph gfg;
|
|
||||||
gfg.push_back(jf);
|
|
||||||
|
|
||||||
Vector c(1);
|
|
||||||
// When hiding c inside the `b` vector, value == 0.5*c^2
|
|
||||||
c << std::sqrt(2.0 * normalized_value);
|
|
||||||
auto constantFactor = std::make_shared<JacobianFactor>(c);
|
|
||||||
|
|
||||||
gfg.push_back(constantFactor);
|
|
||||||
// NOTE(Frank): we store the actual value, not the normalized value:
|
|
||||||
return {std::make_shared<JacobianFactor>(gfg), value};
|
|
||||||
};
|
|
||||||
return FactorValuePairs(factors, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* *******************************************************************************/
|
/* *******************************************************************************/
|
||||||
struct HybridGaussianFactor::ConstructorHelper {
|
struct HybridGaussianFactor::ConstructorHelper {
|
||||||
KeyVector continuousKeys; // Continuous keys extracted from factors
|
KeyVector continuousKeys; // Continuous keys extracted from factors
|
||||||
|
@ -88,10 +49,10 @@ struct HybridGaussianFactor::ConstructorHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Build the FactorValuePairs DecisionTree
|
// Build the FactorValuePairs DecisionTree
|
||||||
pairs = FactorValuePairs(DecisionTree<Key, GaussianFactor::shared_ptr>(discreteKeys, factors),
|
pairs = FactorValuePairs(
|
||||||
[](const auto& f) {
|
DecisionTree<Key, GaussianFactor::shared_ptr>(discreteKeys, factors), [](const auto& f) {
|
||||||
return std::pair{f, 0.0};
|
return std::pair{f, f ? 0.0 : std::numeric_limits<double>::infinity()};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructor for a single discrete key and a vector of GaussianFactorValuePairs
|
/// Constructor for a single discrete key and a vector of GaussianFactorValuePairs
|
||||||
|
@ -128,7 +89,7 @@ struct HybridGaussianFactor::ConstructorHelper {
|
||||||
|
|
||||||
/* *******************************************************************************/
|
/* *******************************************************************************/
|
||||||
HybridGaussianFactor::HybridGaussianFactor(const ConstructorHelper& helper)
|
HybridGaussianFactor::HybridGaussianFactor(const ConstructorHelper& helper)
|
||||||
: Base(helper.continuousKeys, helper.discreteKeys), factors_(augment(helper.pairs)) {}
|
: Base(helper.continuousKeys, helper.discreteKeys), factors_(helper.pairs) {}
|
||||||
|
|
||||||
HybridGaussianFactor::HybridGaussianFactor(
|
HybridGaussianFactor::HybridGaussianFactor(
|
||||||
const DiscreteKey& discreteKey, const std::vector<GaussianFactor::shared_ptr>& factorPairs)
|
const DiscreteKey& discreteKey, const std::vector<GaussianFactor::shared_ptr>& factorPairs)
|
||||||
|
@ -187,8 +148,7 @@ void HybridGaussianFactor::print(const std::string& s, const KeyFormatter& forma
|
||||||
}
|
}
|
||||||
|
|
||||||
/* *******************************************************************************/
|
/* *******************************************************************************/
|
||||||
GaussianFactorValuePair HybridGaussianFactor::operator()(
|
GaussianFactorValuePair HybridGaussianFactor::operator()(const DiscreteValues& assignment) const {
|
||||||
const DiscreteValues& assignment) const {
|
|
||||||
return factors_(assignment);
|
return factors_(assignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,26 +162,13 @@ HybridGaussianProductFactor HybridGaussianFactor::asProductFactor() const {
|
||||||
}}};
|
}}};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* *******************************************************************************/
|
|
||||||
/// Helper method to compute the error of a component.
|
|
||||||
static double PotentiallyPrunedComponentError(const GaussianFactor::shared_ptr& gf,
|
|
||||||
const VectorValues& values) {
|
|
||||||
// Check if valid pointer
|
|
||||||
if (gf) {
|
|
||||||
return gf->error(values);
|
|
||||||
} else {
|
|
||||||
// If nullptr this component was pruned, so we return maximum error. This
|
|
||||||
// way the negative exponential will give a probability value close to 0.0.
|
|
||||||
return std::numeric_limits<double>::max();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* *******************************************************************************/
|
/* *******************************************************************************/
|
||||||
AlgebraicDecisionTree<Key> HybridGaussianFactor::errorTree(
|
AlgebraicDecisionTree<Key> HybridGaussianFactor::errorTree(
|
||||||
const VectorValues& continuousValues) const {
|
const VectorValues& continuousValues) const {
|
||||||
// functor to convert from sharedFactor to double error value.
|
// functor to convert from sharedFactor to double error value.
|
||||||
auto errorFunc = [&continuousValues](const auto& pair) {
|
auto errorFunc = [&continuousValues](const auto& pair) {
|
||||||
return PotentiallyPrunedComponentError(pair.first, continuousValues);
|
return pair.first ? pair.first->error(continuousValues) + pair.second
|
||||||
|
: std::numeric_limits<double>::infinity();
|
||||||
};
|
};
|
||||||
DecisionTree<Key, double> error_tree(factors_, errorFunc);
|
DecisionTree<Key, double> error_tree(factors_, errorFunc);
|
||||||
return error_tree;
|
return error_tree;
|
||||||
|
@ -231,7 +178,8 @@ AlgebraicDecisionTree<Key> HybridGaussianFactor::errorTree(
|
||||||
double HybridGaussianFactor::error(const HybridValues& values) const {
|
double HybridGaussianFactor::error(const HybridValues& values) const {
|
||||||
// Directly index to get the component, no need to build the whole tree.
|
// Directly index to get the component, no need to build the whole tree.
|
||||||
const auto pair = factors_(values.discrete());
|
const auto pair = factors_(values.discrete());
|
||||||
return PotentiallyPrunedComponentError(pair.first, values.continuous());
|
return pair.first ? pair.first->error(values.continuous()) + pair.second
|
||||||
|
: std::numeric_limits<double>::infinity();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace gtsam
|
} // namespace gtsam
|
||||||
|
|
|
@ -154,8 +154,6 @@ void HybridGaussianFactorGraph::printErrors(
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************ */
|
/* ************************************************************************ */
|
||||||
// TODO(dellaert): it's probably more efficient to first collect the discrete
|
|
||||||
// keys, and then loop over all assignments to populate a vector.
|
|
||||||
HybridGaussianProductFactor HybridGaussianFactorGraph::collectProductFactor() const {
|
HybridGaussianProductFactor HybridGaussianFactorGraph::collectProductFactor() const {
|
||||||
HybridGaussianProductFactor result;
|
HybridGaussianProductFactor result;
|
||||||
|
|
||||||
|
@ -270,6 +268,7 @@ static std::shared_ptr<Factor> createDiscreteFactor(const ResultTree& eliminatio
|
||||||
const DiscreteKeys& discreteSeparator) {
|
const DiscreteKeys& discreteSeparator) {
|
||||||
auto potential = [&](const auto& pair) -> double {
|
auto potential = [&](const auto& pair) -> double {
|
||||||
const auto& [conditional, factor] = pair.first;
|
const auto& [conditional, factor] = pair.first;
|
||||||
|
const double scalar = pair.second;
|
||||||
if (conditional && factor) {
|
if (conditional && factor) {
|
||||||
// If the factor is not null, it has no keys, just contains the residual.
|
// If the factor is not null, it has no keys, just contains the residual.
|
||||||
|
|
||||||
|
@ -278,8 +277,8 @@ static std::shared_ptr<Factor> createDiscreteFactor(const ResultTree& eliminatio
|
||||||
// negLogConstant gives `-log(k)`
|
// negLogConstant gives `-log(k)`
|
||||||
// which is `-log(k) = log(1/k) = log(\sqrt{|2πΣ|})`.
|
// which is `-log(k) = log(1/k) = log(\sqrt{|2πΣ|})`.
|
||||||
const double negLogK = conditional->negLogConstant();
|
const double negLogK = conditional->negLogConstant();
|
||||||
const double old = factor->error(kEmpty) - negLogK;
|
const double error = scalar + factor->error(kEmpty) - negLogK;
|
||||||
return exp(-old);
|
return exp(-error);
|
||||||
} else if (!conditional && !factor) {
|
} else if (!conditional && !factor) {
|
||||||
// If the factor is null, it has been pruned, hence return potential of zero
|
// If the factor is null, it has been pruned, hence return potential of zero
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -302,17 +301,10 @@ static std::shared_ptr<Factor> createHybridGaussianFactor(const ResultTree& elim
|
||||||
const auto& [conditional, factor] = pair.first;
|
const auto& [conditional, factor] = pair.first;
|
||||||
const double scalar = pair.second;
|
const double scalar = pair.second;
|
||||||
if (conditional && factor) {
|
if (conditional && factor) {
|
||||||
auto hf = std::dynamic_pointer_cast<HessianFactor>(factor);
|
|
||||||
if (!hf) throw std::runtime_error("Expected HessianFactor!");
|
|
||||||
// Add 2.0 term since the constant term will be premultiplied by 0.5
|
|
||||||
// as per the Hessian definition,
|
|
||||||
// and negative since we want log(k)
|
|
||||||
const double negLogK = conditional->negLogConstant();
|
const double negLogK = conditional->negLogConstant();
|
||||||
hf->constantTerm() += -2.0 * negLogK;
|
return {factor, scalar - negLogK};
|
||||||
return {factor, negLogK};
|
|
||||||
return {factor, scalar + negLogK};
|
|
||||||
} else if (!conditional && !factor) {
|
} else if (!conditional && !factor) {
|
||||||
return {nullptr, 0.0}; // TODO(frank): or should this be infinity?
|
return {nullptr, std::numeric_limits<double>::infinity()};
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("createHybridGaussianFactors has mixed NULLs");
|
throw std::runtime_error("createHybridGaussianFactors has mixed NULLs");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue