Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NoiseModelFactorN - fixed-number of variables >6 #947

Merged
merged 45 commits into from
Dec 23, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
81f1d93
NoiseModelFactorN - fixed-number of variables >6
gchenfc Dec 1, 2021
e037fa1
c++11 doesn't support std::size so use obj.size() instead
gchenfc Dec 1, 2021
d9c8ce2
alternate make_index_sequence impl if no boost::mp11
gchenfc Dec 3, 2021
2aecaf3
optional jacobian overloads backwards compatibility
gchenfc Dec 3, 2021
8fe7e48
backward compatibility unit tests for NoiseModelFactor4
gchenfc Dec 3, 2021
bee4eee
NoiseModelFactor4 implemented as derived class of NoiseModelFactorN
gchenfc Dec 3, 2021
ed07edb
converted all NoiseModelFactorX to inherit from NoiseModelFactorN
gchenfc Dec 3, 2021
ea7d769
documentation
gchenfc Dec 3, 2021
ba3cc85
avoid inheritance by conditionally defining backwards compatibility t…
gchenfc Dec 3, 2021
ddcca4c
switch template bool specialization order
gchenfc Dec 3, 2021
280acde
can't get "using NoiseModelFactorX = NoiseModelFactorN" to work
gchenfc Dec 3, 2021
89b4340
alternate option for typedef-ing X1, X2, ...
gchenfc Dec 8, 2021
5004c47
revert typedef X1, X2, ... to old version, and clean up a little
gchenfc Dec 8, 2021
018213e
switch `using NoiseModelFactorX = ...` to `#define ...`.
gchenfc Dec 9, 2021
84e873e
fix Windows CI issue: VALUE happens to have the same name in PriorFactor
gchenfc Dec 9, 2021
40e585b
review comments
gchenfc Dec 9, 2021
11fd861
update doxygen (review comment)
gchenfc Dec 9, 2021
3addc8d
Merge branch 'develop' into feature/NoiseModelFactorN
gchenfc Jan 30, 2022
c9dbb6e
create backwards compatibility unit test for NoiseModelFactor1
gchenfc Jan 30, 2022
bb33be5
revert some template stuff with inheritance for readability
gchenfc Jan 30, 2022
82e0d20
move boost::index_sequence stuff to utilities file
gchenfc Jan 30, 2022
d62033a
fix namespace collision with symbol_shorthand::X in unit test
gchenfc Jan 30, 2022
a2fb0e4
Revert "create backwards compatibility unit test for NoiseModelFactor1"
gchenfc Jan 31, 2022
1a427cd
Serialize test strings generated with Boost 1.65
gchenfc Jan 31, 2022
6653d66
fix test xml file path
gchenfc Jan 31, 2022
782a894
fix expected serialization string
gchenfc Apr 21, 2022
8ae8c7a
Merge branch 'develop' into feature/NoiseModelFactorN
gchenfc Apr 21, 2022
71767a4
serialization debugging (from stash)
gchenfc Jul 19, 2022
2ea97fb
Merge branch 'develop' into feature/NoiseModelFactorN_test
gchenfc Jul 19, 2022
00cf13b
update serialized string
gchenfc Jul 19, 2022
ea6e32d
bugfix on serialization
gchenfc Jul 19, 2022
8327685
remove debug statements
gchenfc Jul 20, 2022
fa196aa
turn off backwards compatibility test with quaternions or TBB since s…
gchenfc Jul 20, 2022
1127276
Merge branch 'develop' into feature/NoiseModelFactorN (for CI)
gchenfc Jul 21, 2022
322e555
address review comments
gchenfc Nov 16, 2022
94865c4
fix boost 1.65 patch bug
gchenfc Dec 19, 2022
63950b9
Revert "fix namespace collision with symbol_shorthand::X in unit test"
gchenfc Dec 19, 2022
0ebc6e8
Change `X<N>` to `ValueType<N>` and `VALUES` -> `ValueTypes`
gchenfc Dec 19, 2022
b24511f
address review comments
gchenfc Dec 19, 2022
e8ddbbe
Check type of CONTAINER constructor tparam
gchenfc Dec 20, 2022
040eb63
make SFINAE templates more readable
gchenfc Dec 22, 2022
d16d263
better docstrings w/ usage examples
gchenfc Dec 22, 2022
4b93970
Change backwards-compatibility defs to utilize new style
gchenfc Dec 22, 2022
19215af
update and fix unit tests
gchenfc Dec 22, 2022
2a7efc7
Merge branch 'develop' into feature/NoiseModelFactorN_replaceDeprecated
gchenfc Dec 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gtsam/mainpage.dox
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ To use GTSAM to solve your own problems, you will often have to create new facto

-# The number of variables your factor involves is <b>unknown</b> at compile time - derive from NoiseModelFactor and implement NoiseModelFactor::unwhitenedError()
- This is a factor expressing the sum-of-squares error between a measurement \f$ z \f$ and a measurement prediction function \f$ h(x) \f$, on which the errors are expected to follow some distribution specified by a noise model (see noiseModel).
-# The number of variables your factor involves is <b>known</b> at compile time and is between 1 and 6 - derive from NoiseModelFactor1, NoiseModelFactor2, NoiseModelFactor3, NoiseModelFactor4, NoiseModelFactor5, or NoiseModelFactor6, and implement <b>\c evaluateError()</b>
-# The number of variables your factor involves is <b>known</b> at compile time and is between 1 and 6 - derive from NoiseModelFactor1, NoiseModelFactor2, NoiseModelFactor3, NoiseModelFactor4, NoiseModelFactor5, or NoiseModelFactor6, and implement <b>\c evaluateError()</b>. If the number of variables is greater than 6, derive from NoiseModelFactorN.
gchenfc marked this conversation as resolved.
Show resolved Hide resolved
- This factor expresses the same sum-of-squares error with a noise model, but makes the implementation task slightly easier than with %NoiseModelFactor.
-# Derive from NonlinearFactor
- This is more advanced and allows creating factors without an explicit noise model, or that linearize to HessianFactor instead of JacobianFactor.
Expand Down
106 changes: 106 additions & 0 deletions gtsam/nonlinear/NonlinearFactor.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include <boost/serialization/base_object.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/mp11/integer_sequence.hpp>

namespace gtsam {

Expand Down Expand Up @@ -770,5 +771,110 @@ class NoiseModelFactor6: public NoiseModelFactor {
}; // \class NoiseModelFactor6

/* ************************************************************************* */
/** A convenient base class for creating your own NoiseModelFactor with N
* variables. To derive from this class, implement evaluateError(). */
template<class... VALUES>
class NoiseModelFactorN: public NoiseModelFactor {

protected:

typedef NoiseModelFactor Base;
typedef NoiseModelFactorN<VALUES...> This;

/* "Dummy templated" alias is used to expand fixed-type parameter packs with
* same length as VALUES. This ignores the template parameter. */
template <typename T>
using optional_matrix_type = boost::optional<Matrix&>;

/* "Dummy templated" alias is used to expand fixed-type parameter packs with
* same length as VALUES. This ignores the template parameter. */
template <typename T>
using key_type = Key;

public:

/**
* Default Constructor for I/O
*/
NoiseModelFactorN() {}

/**
* Constructor.
* Example usage: NoiseModelFactorN(noise, key1, key2, ..., keyN)
* @param noiseModel shared pointer to noise model
* @param keys... keys for the variables in this factor
*/
NoiseModelFactorN(const SharedNoiseModel& noiseModel,
key_type<VALUES>... keys)
: Base(noiseModel, std::array<Key, sizeof...(VALUES)>{keys...}) {}

/**
* Constructor.
* @param noiseModel shared pointer to noise model
* @param keys a container of keys for the variables in this factor
*/
template <typename CONTAINER>
NoiseModelFactorN(const SharedNoiseModel& noiseModel, CONTAINER keys)
: Base(noiseModel, keys) {
assert(keys.size() == sizeof...(VALUES));
}

~NoiseModelFactorN() override {}

/** Method to retrieve keys */
template <int N>
inline Key key() const { return keys_[N]; }

/** Calls the n-key specific version of evaluateError, which is pure virtual
* so must be implemented in the derived class. */
Vector unwhitenedError(
const Values& x,
boost::optional<std::vector<Matrix>&> H = boost::none) const override {
return unwhitenedError(boost::mp11::index_sequence_for<VALUES...>{}, x, H);
}

/**
* Override this method to finish implementing an n-way factor.
* If any of the optional Matrix reference arguments are specified, it should
* compute both the function evaluation and its derivative(s) in the requested
* variables.
*/
virtual Vector evaluateError(
const VALUES& ... x,
optional_matrix_type<VALUES> ... H) const = 0;

/** No-jacobians requested function overload (since parameter packs can't have
* default args) */
Vector evaluateError(const VALUES&... x) const {
return evaluateError(x..., optional_matrix_type<VALUES>()...);
}

private:

/** Pack expansion with index_sequence template pattern */
template <std::size_t... Inds>
Vector unwhitenedError(
boost::mp11::index_sequence<Inds...>, //
const Values& x,
boost::optional<std::vector<Matrix>&> H = boost::none) const {
if (this->active(x)) {
if (H) {
return evaluateError(x.at<VALUES>(keys_[Inds])..., (*H)[Inds]...);
} else {
return evaluateError(x.at<VALUES>(keys_[Inds])...);
}
} else {
return Vector::Zero(this->dim());
}
}

/** Serialization function */
friend class boost::serialization::access;
template<class ARCHIVE>
void serialize(ARCHIVE & ar, const unsigned int /*version*/) {
ar & boost::serialization::make_nvp("NoiseModelFactor",
boost::serialization::base_object<Base>(*this));
}
}; // \class NoiseModelFactorN

} // \namespace gtsam
44 changes: 44 additions & 0 deletions tests/testNonlinearFactor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,50 @@ TEST(NonlinearFactor, NoiseModelFactor6) {

}

/* ************************************************************************* */
class TestFactorN : public NoiseModelFactorN<double, double, double, double> {
public:
typedef NoiseModelFactorN<double, double, double, double> Base;
TestFactorN() : Base(noiseModel::Diagonal::Sigmas((Vector(1) << 2.0).finished()), X(1), X(2), X(3), X(4)) {}

Vector
evaluateError(const double& x1, const double& x2, const double& x3, const double& x4,
boost::optional<Matrix&> H1 = boost::none,
boost::optional<Matrix&> H2 = boost::none,
boost::optional<Matrix&> H3 = boost::none,
boost::optional<Matrix&> H4 = boost::none) const override {
if (H1) {
*H1 = (Matrix(1, 1) << 1.0).finished();
*H2 = (Matrix(1, 1) << 2.0).finished();
*H3 = (Matrix(1, 1) << 3.0).finished();
*H4 = (Matrix(1, 1) << 4.0).finished();
}
return (Vector(1) << x1 + x2 + x3 + x4).finished();
}
};

/* ************************************ */
TEST(NonlinearFactor, NoiseModelFactorN) {
TestFactorN tf;
Values tv;
tv.insert(X(1), double((1.0)));
tv.insert(X(2), double((2.0)));
tv.insert(X(3), double((3.0)));
tv.insert(X(4), double((4.0)));
EXPECT(assert_equal((Vector(1) << 10.0).finished(), tf.unwhitenedError(tv)));
DOUBLES_EQUAL(25.0/2.0, tf.error(tv), 1e-9);
JacobianFactor jf(*boost::dynamic_pointer_cast<JacobianFactor>(tf.linearize(tv)));
LONGS_EQUAL((long)X(1), (long)jf.keys()[0]);
LONGS_EQUAL((long)X(2), (long)jf.keys()[1]);
LONGS_EQUAL((long)X(3), (long)jf.keys()[2]);
LONGS_EQUAL((long)X(4), (long)jf.keys()[3]);
EXPECT(assert_equal((Matrix)(Matrix(1, 1) << 0.5).finished(), jf.getA(jf.begin())));
EXPECT(assert_equal((Matrix)(Matrix(1, 1) << 1.0).finished(), jf.getA(jf.begin()+1)));
EXPECT(assert_equal((Matrix)(Matrix(1, 1) << 1.5).finished(), jf.getA(jf.begin()+2)));
EXPECT(assert_equal((Matrix)(Matrix(1, 1) << 2.0).finished(), jf.getA(jf.begin()+3)));
EXPECT(assert_equal((Vector)(Vector(1) << -5.0).finished(), jf.getb()));
}

/* ************************************************************************* */
TEST( NonlinearFactor, clone_rekey )
{
Expand Down