Add support for bounds to ParameterBlock.
Add setters and getters for lower and upper bounds.
Generalize the Plus operation to include projection onto the
hypercube implied by the bounds.
Change-Id: I1e4028a9886c4064f31bbc5b7c22b0341a56c15d
diff --git a/internal/ceres/parameter_block.h b/internal/ceres/parameter_block.h
index 695fa6f..c3fcce3 100644
--- a/internal/ceres/parameter_block.h
+++ b/internal/ceres/parameter_block.h
@@ -31,7 +31,9 @@
#ifndef CERES_INTERNAL_PARAMETER_BLOCK_H_
#define CERES_INTERNAL_PARAMETER_BLOCK_H_
+#include <algorithm>
#include <cstdlib>
+#include <limits>
#include <string>
#include "ceres/array_utils.h"
#include "ceres/collections_port.h"
@@ -180,16 +182,37 @@
}
}
+ void SetUpperBound(int index, double upper_bound) {
+ CHECK_LT(index, size_);
+ upper_bounds_[index] = upper_bound;
+ };
+
+ void SetLowerBound(int index, double lower_bound) {
+ CHECK_LT(index, size_);
+ lower_bounds_[index] = lower_bound;
+ }
+
// Generalization of the addition operation. This is the same as
- // LocalParameterization::Plus() but uses the parameter's current state
- // instead of operating on a passed in pointer.
+ // LocalParameterization::Plus() followed by projection onto the
+ // hyper cube implied by the bounds constraints.
bool Plus(const double *x, const double* delta, double* x_plus_delta) {
- if (local_parameterization_ == NULL) {
+ if (local_parameterization_ != NULL) {
+ if (!local_parameterization_->Plus(x, delta, x_plus_delta)) {
+ return false;
+ }
+ } else {
VectorRef(x_plus_delta, size_) = ConstVectorRef(x, size_) +
ConstVectorRef(delta, size_);
- return true;
}
- return local_parameterization_->Plus(x, delta, x_plus_delta);
+
+ // Project onto the box constraints.
+ for (int i = 0; i < size_; ++i) {
+ x_plus_delta[i] = std::min(std::max(x_plus_delta[i],
+ lower_bounds_[i]),
+ upper_bounds_[i]);
+ }
+
+ return true;
}
string ToString() const {
@@ -234,6 +257,14 @@
return residual_blocks_.get();
}
+ const double* upper_bounds() const {
+ return upper_bounds_.get();
+ }
+
+ const double* lower_bounds() const {
+ return lower_bounds_.get();
+ }
+
private:
void Init(double* user_state,
int size,
@@ -250,6 +281,15 @@
SetParameterization(local_parameterization);
}
+ upper_bounds_.reset(new double[size_]);
+ std::fill(upper_bounds_.get(),
+ upper_bounds_.get() + size_,
+ std::numeric_limits<double>::max());
+ lower_bounds_.reset(new double[size_]);
+ std::fill(lower_bounds_.get(),
+ lower_bounds_.get() + size_,
+ -std::numeric_limits<double>::max());
+
state_offset_ = -1;
delta_offset_ = -1;
}
@@ -312,6 +352,9 @@
// If non-null, contains the residual blocks this parameter block is in.
scoped_ptr<ResidualBlockSet> residual_blocks_;
+ scoped_array<double> upper_bounds_;
+ scoped_array<double> lower_bounds_;
+
// Necessary so ProblemImpl can clean up the parameterizations.
friend class ProblemImpl;
};
diff --git a/internal/ceres/parameter_block_test.cc b/internal/ceres/parameter_block_test.cc
index 09156f8..a76c408 100644
--- a/internal/ceres/parameter_block_test.cc
+++ b/internal/ceres/parameter_block_test.cc
@@ -169,5 +169,42 @@
EXPECT_FALSE(parameter_block.SetState(&y));
}
+TEST(ParameterBlock, DefaultBounds) {
+ double x[2];
+ ParameterBlock parameter_block(x, 2, -1, NULL);
+ const double* upper_bounds = parameter_block.upper_bounds();
+ EXPECT_EQ(upper_bounds[0], std::numeric_limits<double>::max());
+ EXPECT_EQ(upper_bounds[1], std::numeric_limits<double>::max());
+ const double* lower_bounds = parameter_block.lower_bounds();
+ EXPECT_EQ(lower_bounds[0], -std::numeric_limits<double>::max());
+ EXPECT_EQ(lower_bounds[1], -std::numeric_limits<double>::max());
+}
+
+TEST(ParameterBlock, SetBounds) {
+ double x[2];
+ ParameterBlock parameter_block(x, 2, -1, NULL);
+ parameter_block.SetUpperBound(1, 1);
+ parameter_block.SetLowerBound(0, 1);
+
+ const double* upper_bounds = parameter_block.upper_bounds();
+ EXPECT_EQ(upper_bounds[0], std::numeric_limits<double>::max());
+ EXPECT_EQ(upper_bounds[1], 1.0);
+ const double* lower_bounds = parameter_block.lower_bounds();
+ EXPECT_EQ(lower_bounds[0], 1.0);
+ EXPECT_EQ(lower_bounds[1], -std::numeric_limits<double>::max());
+}
+
+TEST(ParameterBlock, PlusWithBoundsConstraints) {
+ double x[] = {1.0, 0.0};
+ double delta[] = {2.0, -10.0};
+ ParameterBlock parameter_block(x, 2, -1, NULL);
+ parameter_block.SetUpperBound(0, 2.0);
+ parameter_block.SetLowerBound(1, -1.0);
+ double x_plus_delta[2];
+ parameter_block.Plus(x, delta, x_plus_delta);
+ EXPECT_EQ(x_plus_delta[0], 2.0);
+ EXPECT_EQ(x_plus_delta[1], -1.0);
+}
+
} // namespace internal
} // namespace ceres