SQPOptimizer can now do its own initialization of the Lagrange multipliers.
Cleaned up NonlinearConstraintrelease/4.3a0
parent
a5515d9d57
commit
31856ce598
|
@ -15,6 +15,21 @@ namespace gtsam {
|
||||||
// Implementations of unary nonlinear constraints
|
// Implementations of unary nonlinear constraints
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
||||||
|
template <class Config>
|
||||||
|
NonlinearConstraint1<Config>::NonlinearConstraint1(
|
||||||
|
const std::string& key,
|
||||||
|
Matrix (*gradG)(const Config& config, const std::string& key),
|
||||||
|
Vector (*g)(const Config& config, const std::string& key),
|
||||||
|
size_t dim_constraint,
|
||||||
|
const std::string& lagrange_key) :
|
||||||
|
NonlinearConstraint<Config>(lagrange_key, dim_constraint),
|
||||||
|
g_(g), gradG_(gradG), key_(key) {
|
||||||
|
// set a good lagrange key here
|
||||||
|
// TODO:should do something smart to find a unique one
|
||||||
|
if (lagrange_key == "")
|
||||||
|
this->lagrange_key_ = "L_" + key;
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
template <class Config>
|
template <class Config>
|
||||||
void NonlinearConstraint1<Config>::print(const std::string& s) const {
|
void NonlinearConstraint1<Config>::print(const std::string& s) const {
|
||||||
|
@ -64,6 +79,24 @@ NonlinearConstraint1<Config>::linearize(const Config& config, const VectorConfig
|
||||||
// Implementations of binary nonlinear constraints
|
// Implementations of binary nonlinear constraints
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
template <class Config>
|
||||||
|
NonlinearConstraint2<Config>::NonlinearConstraint2(
|
||||||
|
const std::string& key1,
|
||||||
|
Matrix (*gradG1)(const Config& config, const std::string& key),
|
||||||
|
const std::string& key2,
|
||||||
|
Matrix (*gradG2)(const Config& config, const std::string& key),
|
||||||
|
Vector (*g)(const Config& config, const std::string& key1, const std::string& key2),
|
||||||
|
size_t dim_constraint,
|
||||||
|
const std::string& lagrange_key) :
|
||||||
|
NonlinearConstraint<Config>(lagrange_key, dim_constraint),
|
||||||
|
g_(g), gradG1_(gradG1), gradG2_(gradG2), key1_(key1), key2_(key2) {
|
||||||
|
// set a good lagrange key here
|
||||||
|
// TODO:should do something smart to find a unique one
|
||||||
|
if (lagrange_key == "")
|
||||||
|
this->lagrange_key_ = "L_" + key1 + key2;
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
template <class Config>
|
template <class Config>
|
||||||
void NonlinearConstraint2<Config>::print(const std::string& s) const {
|
void NonlinearConstraint2<Config>::print(const std::string& s) const {
|
||||||
|
|
|
@ -43,7 +43,7 @@ public:
|
||||||
lagrange_key_(lagrange_key), p_(dim_lagrange) {}
|
lagrange_key_(lagrange_key), p_(dim_lagrange) {}
|
||||||
|
|
||||||
/** returns the key used for the Lagrange multipliers */
|
/** returns the key used for the Lagrange multipliers */
|
||||||
std::string& lagrangeKey() const { return lagrange_key_; }
|
std::string lagrangeKey() const { return lagrange_key_; }
|
||||||
|
|
||||||
/** returns the number of lagrange multipliers */
|
/** returns the number of lagrange multipliers */
|
||||||
size_t nrConstraints() const { return p_; }
|
size_t nrConstraints() const { return p_; }
|
||||||
|
@ -120,14 +120,7 @@ public:
|
||||||
Matrix (*gradG)(const Config& config, const std::string& key),
|
Matrix (*gradG)(const Config& config, const std::string& key),
|
||||||
Vector (*g)(const Config& config, const std::string& key),
|
Vector (*g)(const Config& config, const std::string& key),
|
||||||
size_t dim_constraint,
|
size_t dim_constraint,
|
||||||
const std::string& lagrange_key="") :
|
const std::string& lagrange_key="");
|
||||||
NonlinearConstraint<Config>(lagrange_key, dim_constraint),
|
|
||||||
g_(g), gradG_(gradG), key_(key) {
|
|
||||||
// set a good lagrange key here
|
|
||||||
// TODO:should do something smart to find a unique one
|
|
||||||
if (lagrange_key == "")
|
|
||||||
this->lagrange_key_ = "L_" + key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Print */
|
/** Print */
|
||||||
void print(const std::string& s = "") const;
|
void print(const std::string& s = "") const;
|
||||||
|
@ -199,14 +192,7 @@ public:
|
||||||
Matrix (*gradG2)(const Config& config, const std::string& key),
|
Matrix (*gradG2)(const Config& config, const std::string& key),
|
||||||
Vector (*g)(const Config& config, const std::string& key1, const std::string& key2),
|
Vector (*g)(const Config& config, const std::string& key1, const std::string& key2),
|
||||||
size_t dim_constraint,
|
size_t dim_constraint,
|
||||||
const std::string& lagrange_key="") :
|
const std::string& lagrange_key="");
|
||||||
NonlinearConstraint<Config>(lagrange_key, dim_constraint),
|
|
||||||
g_(g), gradG1_(gradG1), gradG2_(gradG2), key1_(key1), key2_(key2) {
|
|
||||||
// set a good lagrange key here
|
|
||||||
// TODO:should do something smart to find a unique one
|
|
||||||
if (lagrange_key == "")
|
|
||||||
this->lagrange_key_ = "L_" + key1 + key2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Print */
|
/** Print */
|
||||||
void print(const std::string& s = "") const;
|
void print(const std::string& s = "") const;
|
||||||
|
|
|
@ -7,27 +7,48 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
#include <boost/assign/std/list.hpp> // for operator +=
|
||||||
|
#include <boost/assign/std/map.hpp> // for insert
|
||||||
#include "GaussianFactorGraph.h"
|
#include "GaussianFactorGraph.h"
|
||||||
#include "SQPOptimizer.h"
|
#include "SQPOptimizer.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace boost::assign;
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
/* **************************************************************** */
|
/* **************************************************************** */
|
||||||
template <class G, class C>
|
template <class G, class C>
|
||||||
SQPOptimizer<G,C>::SQPOptimizer(const G& graph, const Ordering& ordering,
|
SQPOptimizer<G,C>::SQPOptimizer(const G& graph, const Ordering& ordering,
|
||||||
shared_config config)
|
shared_config config)
|
||||||
: graph_(&graph), ordering_(&ordering), config_(config)
|
: graph_(&graph), ordering_(&ordering), full_ordering_(ordering),
|
||||||
|
config_(config), lagrange_config_(new VectorConfig)
|
||||||
{
|
{
|
||||||
// TODO: assign a value to the lagrange config
|
// local typedefs
|
||||||
|
typedef typename G::const_iterator const_iterator;
|
||||||
|
typedef NonlinearConstraint<C> NLConstraint;
|
||||||
|
typedef boost::shared_ptr<NLConstraint > shared_c;
|
||||||
|
|
||||||
|
// find the constraints
|
||||||
|
for (const_iterator factor = graph_->begin(); factor < graph_->end(); factor++) {
|
||||||
|
const shared_c constraint = boost::shared_dynamic_cast<NLConstraint >(*factor);
|
||||||
|
if (constraint != NULL) {
|
||||||
|
size_t p = constraint->nrConstraints();
|
||||||
|
// update ordering
|
||||||
|
string key = constraint->lagrangeKey();
|
||||||
|
full_ordering_ += key;
|
||||||
|
// initialize lagrange multipliers
|
||||||
|
lagrange_config_->insert(key, ones(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* **************************************************************** */
|
/* **************************************************************** */
|
||||||
template <class G, class C>
|
template <class G, class C>
|
||||||
SQPOptimizer<G,C>::SQPOptimizer(const G& graph, const Ordering& ordering,
|
SQPOptimizer<G,C>::SQPOptimizer(const G& graph, const Ordering& ordering,
|
||||||
shared_config config, shared_vconfig lagrange)
|
shared_config config, shared_vconfig lagrange)
|
||||||
: graph_(&graph), ordering_(&ordering), config_(config), lagrange_config_(lagrange)
|
: graph_(&graph), ordering_(&ordering), full_ordering_(ordering),
|
||||||
|
config_(config), lagrange_config_(lagrange)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -66,7 +87,7 @@ SQPOptimizer<G, C> SQPOptimizer<G, C>::iterate(Verbosity v) const {
|
||||||
if (verbose) fg.print("Before Optimization");
|
if (verbose) fg.print("Before Optimization");
|
||||||
|
|
||||||
// optimize linear graph to get full delta config
|
// optimize linear graph to get full delta config
|
||||||
VectorConfig delta = fg.optimize(*ordering_).scale(-1.0);
|
VectorConfig delta = fg.optimize(full_ordering_).scale(-1.0);
|
||||||
|
|
||||||
if (verbose) delta.print("Delta Config");
|
if (verbose) delta.print("Delta Config");
|
||||||
|
|
||||||
|
@ -75,7 +96,7 @@ SQPOptimizer<G, C> SQPOptimizer<G, C>::iterate(Verbosity v) const {
|
||||||
shared_vconfig newLamConfig(new VectorConfig(lagrange_config_->exmap(delta)));
|
shared_vconfig newLamConfig(new VectorConfig(lagrange_config_->exmap(delta)));
|
||||||
|
|
||||||
// construct a new optimizer
|
// construct a new optimizer
|
||||||
return SQPOptimizer<G, C>(*graph_, *ordering_, newConfig, newLamConfig);
|
return SQPOptimizer<G, C>(*graph_, full_ordering_, newConfig, newLamConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,16 +27,22 @@ public:
|
||||||
|
|
||||||
// useful for storing configurations
|
// useful for storing configurations
|
||||||
typedef boost::shared_ptr<const Config> shared_config;
|
typedef boost::shared_ptr<const Config> shared_config;
|
||||||
typedef boost::shared_ptr<const VectorConfig> shared_vconfig;
|
typedef boost::shared_ptr<VectorConfig> shared_vconfig;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// keep const references to the graph and the original ordering
|
// keep const references to the graph and initial ordering
|
||||||
const FactorGraph* graph_;
|
const FactorGraph* graph_;
|
||||||
const Ordering* ordering_;
|
const Ordering* ordering_;
|
||||||
|
|
||||||
// keep configurations
|
// keep configurations
|
||||||
shared_config config_;
|
shared_config config_;
|
||||||
shared_vconfig lagrange_config_;
|
shared_vconfig lagrange_config_;
|
||||||
|
|
||||||
|
// keep a configuration that has been updated to include the lagrange multipliers
|
||||||
|
Ordering full_ordering_;
|
||||||
|
|
||||||
|
|
||||||
|
// keep a set of errors for the overall system and just the constraints
|
||||||
double error_;
|
double error_;
|
||||||
double constraint_error_;
|
double constraint_error_;
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ TEST ( SQPOptimizer, basic ) {
|
||||||
CHECK(assert_equal(*config, *(optimizer.config())));
|
CHECK(assert_equal(*config, *(optimizer.config())));
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace sqpOptimizer_test1 {
|
namespace sqp_LinearMapWarp2 {
|
||||||
// binary constraint between landmarks
|
// binary constraint between landmarks
|
||||||
/** g(x) = x-y = 0 */
|
/** g(x) = x-y = 0 */
|
||||||
Vector g_func(const VectorConfig& config, const std::string& key1, const std::string& key2) {
|
Vector g_func(const VectorConfig& config, const std::string& key1, const std::string& key2) {
|
||||||
|
@ -59,9 +59,9 @@ Matrix grad_g1(const VectorConfig& config, const std::string& key) {
|
||||||
Matrix grad_g2(const VectorConfig& config, const std::string& key) {
|
Matrix grad_g2(const VectorConfig& config, const std::string& key) {
|
||||||
return -1*eye(2);
|
return -1*eye(2);
|
||||||
}
|
}
|
||||||
} // \namespace sqpOptimizer_test1
|
} // \namespace sqp_LinearMapWarp2
|
||||||
|
|
||||||
namespace sqpOptimizer_test2 {
|
namespace sqp_LinearMapWarp1 {
|
||||||
// Unary Constraint on x1
|
// Unary Constraint on x1
|
||||||
/** g(x) = x -[1;1] = 0 */
|
/** g(x) = x -[1;1] = 0 */
|
||||||
Vector g_func(const VectorConfig& config, const std::string& key) {
|
Vector g_func(const VectorConfig& config, const std::string& key) {
|
||||||
|
@ -72,24 +72,16 @@ Vector g_func(const VectorConfig& config, const std::string& key) {
|
||||||
Matrix grad_g(const VectorConfig& config, const std::string& key) {
|
Matrix grad_g(const VectorConfig& config, const std::string& key) {
|
||||||
return eye(2);
|
return eye(2);
|
||||||
}
|
}
|
||||||
} // \namespace sqpOptimizer_test2
|
} // \namespace sqp_LinearMapWarp12
|
||||||
|
|
||||||
typedef SQPOptimizer<NLGraph, VectorConfig> Optimizer;
|
typedef SQPOptimizer<NLGraph, VectorConfig> Optimizer;
|
||||||
|
|
||||||
/**
|
NLGraph linearMapWarpGraph() {
|
||||||
* Test pulled from testSQP, with
|
|
||||||
*/
|
|
||||||
TEST ( SQPOptimizer, simple_case ) {
|
|
||||||
bool verbose = false;
|
|
||||||
// position (1, 1) constraint for x1
|
|
||||||
VectorConfig feas;
|
|
||||||
feas.insert("x1", Vector_(2, 1.0, 1.0));
|
|
||||||
|
|
||||||
// constant constraint on x1
|
// constant constraint on x1
|
||||||
boost::shared_ptr<NonlinearConstraint1<VectorConfig> > c1(
|
boost::shared_ptr<NonlinearConstraint1<VectorConfig> > c1(
|
||||||
new NonlinearConstraint1<VectorConfig>(
|
new NonlinearConstraint1<VectorConfig>(
|
||||||
"x1", *sqpOptimizer_test2::grad_g,
|
"x1", *sqp_LinearMapWarp1::grad_g,
|
||||||
*sqpOptimizer_test2::g_func, 2, "L_x1"));
|
*sqp_LinearMapWarp1::g_func, 2, "L_x1"));
|
||||||
|
|
||||||
// measurement from x1 to l1
|
// measurement from x1 to l1
|
||||||
Vector z1 = Vector_(2, 0.0, 5.0);
|
Vector z1 = Vector_(2, 0.0, 5.0);
|
||||||
|
@ -104,9 +96,9 @@ TEST ( SQPOptimizer, simple_case ) {
|
||||||
// equality constraint between l1 and l2
|
// equality constraint between l1 and l2
|
||||||
boost::shared_ptr<NonlinearConstraint2<VectorConfig> > c2(
|
boost::shared_ptr<NonlinearConstraint2<VectorConfig> > c2(
|
||||||
new NonlinearConstraint2<VectorConfig>(
|
new NonlinearConstraint2<VectorConfig>(
|
||||||
"l1", *sqpOptimizer_test1::grad_g1,
|
"l1", *sqp_LinearMapWarp2::grad_g1,
|
||||||
"l2", *sqpOptimizer_test1::grad_g2,
|
"l2", *sqp_LinearMapWarp2::grad_g2,
|
||||||
*sqpOptimizer_test1::g_func, 2, "L_l1l2"));
|
*sqp_LinearMapWarp2::g_func, 2, "L_l1l2"));
|
||||||
|
|
||||||
// construct the graph
|
// construct the graph
|
||||||
NLGraph graph;
|
NLGraph graph;
|
||||||
|
@ -115,9 +107,19 @@ TEST ( SQPOptimizer, simple_case ) {
|
||||||
graph.push_back(f1);
|
graph.push_back(f1);
|
||||||
graph.push_back(f2);
|
graph.push_back(f2);
|
||||||
|
|
||||||
|
return graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********************************************************************* */
|
||||||
|
TEST ( SQPOptimizer, map_warp_initLam ) {
|
||||||
|
bool verbose = false;
|
||||||
|
// get a graph
|
||||||
|
NLGraph graph = linearMapWarpGraph();
|
||||||
|
|
||||||
// create an initial estimate
|
// create an initial estimate
|
||||||
shared_config initialEstimate(new VectorConfig(feas)); // must start with feasible set
|
shared_config initialEstimate(new VectorConfig);
|
||||||
initialEstimate->insert("l1", Vector_(2, 1.0, 6.0)); // ground truth
|
initialEstimate->insert("x1", Vector_(2, 1.0, 1.0));
|
||||||
|
initialEstimate->insert("l1", Vector_(2, 1.0, 6.0));
|
||||||
initialEstimate->insert("l2", Vector_(2, -4.0, 0.0)); // starting with a separate reference frame
|
initialEstimate->insert("l2", Vector_(2, -4.0, 0.0)); // starting with a separate reference frame
|
||||||
initialEstimate->insert("x2", Vector_(2, 0.0, 0.0)); // other pose starts at origin
|
initialEstimate->insert("x2", Vector_(2, 0.0, 0.0)); // other pose starts at origin
|
||||||
|
|
||||||
|
@ -146,6 +148,38 @@ TEST ( SQPOptimizer, simple_case ) {
|
||||||
CHECK(assert_equal(actual, expected));
|
CHECK(assert_equal(actual, expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ********************************************************************* */
|
||||||
|
TEST ( SQPOptimizer, map_warp ) {
|
||||||
|
// get a graph
|
||||||
|
NLGraph graph = linearMapWarpGraph();
|
||||||
|
|
||||||
|
// create an initial estimate
|
||||||
|
shared_config initialEstimate(new VectorConfig);
|
||||||
|
initialEstimate->insert("x1", Vector_(2, 1.0, 1.0));
|
||||||
|
initialEstimate->insert("l1", Vector_(2, 1.0, 6.0));
|
||||||
|
initialEstimate->insert("l2", Vector_(2, -4.0, 0.0)); // starting with a separate reference frame
|
||||||
|
initialEstimate->insert("x2", Vector_(2, 0.0, 0.0)); // other pose starts at origin
|
||||||
|
|
||||||
|
// create an ordering
|
||||||
|
Ordering ordering;
|
||||||
|
ordering += "x1", "x2", "l1", "l2";
|
||||||
|
|
||||||
|
// create an optimizer
|
||||||
|
Optimizer optimizer(graph, ordering, initialEstimate);
|
||||||
|
|
||||||
|
// perform an iteration of optimization
|
||||||
|
Optimizer oneIteration = optimizer.iterate(Optimizer::SILENT);
|
||||||
|
|
||||||
|
// get the config back out and verify
|
||||||
|
VectorConfig actual = *(oneIteration.config());
|
||||||
|
VectorConfig expected;
|
||||||
|
expected.insert("x1", Vector_(2, 1.0, 1.0));
|
||||||
|
expected.insert("l1", Vector_(2, 1.0, 6.0));
|
||||||
|
expected.insert("l2", Vector_(2, 1.0, 6.0));
|
||||||
|
expected.insert("x2", Vector_(2, 5.0, 6.0));
|
||||||
|
CHECK(assert_equal(actual, expected));
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
int main() { TestResult tr; return TestRegistry::runAllTests(tr); }
|
int main() { TestResult tr; return TestRegistry::runAllTests(tr); }
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
Loading…
Reference in New Issue