Skip to content
This repository has been archived by the owner on Oct 26, 2018. It is now read-only.

Commit

Permalink
Lazily initialize the bounds arrays in ParameterBlock.
Browse files Browse the repository at this point in the history
Problems that do not use bounds do not have to pay the
price of storing bounds constraints.

Also replace the raw pointer access to the upper and
lower bounds arrays with accessors which hides the
lazy initialization from the user.

Change-Id: I0325a35de9c29f853559f891e32e7c777686e537
  • Loading branch information
sandwichmaker committed May 5, 2014
1 parent 633b50b commit a536ae7
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 50 deletions.
66 changes: 47 additions & 19 deletions internal/ceres/parameter_block.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,27 @@ class ParameterBlock {

void SetUpperBound(int index, double upper_bound) {
CHECK_LT(index, size_);

if (upper_bounds_.get() == NULL) {
upper_bounds_.reset(new double[size_]);
std::fill(upper_bounds_.get(),
upper_bounds_.get() + size_,
std::numeric_limits<double>::max());
}

upper_bounds_[index] = upper_bound;
};

void SetLowerBound(int index, double lower_bound) {
CHECK_LT(index, size_);

if (lower_bounds_.get() == NULL) {
lower_bounds_.reset(new double[size_]);
std::fill(lower_bounds_.get(),
lower_bounds_.get() + size_,
-std::numeric_limits<double>::max());
}

lower_bounds_[index] = lower_bound;
}

Expand All @@ -206,10 +222,16 @@ class ParameterBlock {
}

// 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]);
if (lower_bounds_.get() != NULL) {
for (int i = 0; i < size_; ++i) {
x_plus_delta[i] = std::max(x_plus_delta[i], lower_bounds_[i]);
}
}

if (upper_bounds_.get() != NULL) {
for (int i = 0; i < size_; ++i) {
x_plus_delta[i] = std::min(x_plus_delta[i], upper_bounds_[i]);
}
}

return true;
Expand Down Expand Up @@ -257,12 +279,20 @@ class ParameterBlock {
return residual_blocks_.get();
}

const double* upper_bounds() const {
return upper_bounds_.get();
double LowerBoundForParameter(int index) const {
if (lower_bounds_.get() == NULL) {
return -std::numeric_limits<double>::max();
} else {
return lower_bounds_[index];
}
}

const double* lower_bounds() const {
return lower_bounds_.get();
double UpperBoundForParameter(int index) const {
if (upper_bounds_.get() == NULL) {
return std::numeric_limits<double>::max();
} else {
return upper_bounds_[index];
}
}

private:
Expand All @@ -281,15 +311,6 @@ class ParameterBlock {
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;
}
Expand Down Expand Up @@ -352,8 +373,15 @@ class ParameterBlock {
// If non-null, contains the residual blocks this parameter block is in.
scoped_ptr<ResidualBlockSet> residual_blocks_;

// Upper and lower bounds for the parameter block. These arrays are
// initialized to std::numeric_limits<double>::max() and
// Upper and lower bounds for the parameter block. SetUpperBound
// and SetLowerBound lazily initialize the upper_bounds_ and
// lower_bounds_ arrays. If they are never called, then memory for
// these arrays is never allocated. Thus for problems where there
// are no bounds, or only one sided bounds we do not pay the cost of
// allocating memory for the inactive bounds constraints.
//
// Upon initialization these arrays are initialized to
// std::numeric_limits<double>::max() and
// -std::numeric_limits<double>::max() respectively which correspond
// to the parameter block being unconstrained.
scoped_array<double> upper_bounds_;
Expand Down
29 changes: 16 additions & 13 deletions internal/ceres/parameter_block_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -172,26 +172,29 @@ TEST(ParameterBlock, DetectBadLocalParameterization) {
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());
EXPECT_EQ(parameter_block.UpperBoundForParameter(0),
std::numeric_limits<double>::max());
EXPECT_EQ(parameter_block.UpperBoundForParameter(1),
std::numeric_limits<double>::max());
EXPECT_EQ(parameter_block.LowerBoundForParameter(0),
-std::numeric_limits<double>::max());
EXPECT_EQ(parameter_block.LowerBoundForParameter(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);
parameter_block.SetUpperBound(1, 1);

EXPECT_EQ(parameter_block.LowerBoundForParameter(0), 1.0);
EXPECT_EQ(parameter_block.LowerBoundForParameter(1),
-std::numeric_limits<double>::max());

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());
EXPECT_EQ(parameter_block.UpperBoundForParameter(0),
std::numeric_limits<double>::max());
EXPECT_EQ(parameter_block.UpperBoundForParameter(1), 1.0);
}

TEST(ParameterBlock, PlusWithBoundsConstraints) {
Expand Down
39 changes: 21 additions & 18 deletions internal/ceres/solver_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -328,16 +328,16 @@ bool LineSearchOptionsAreValid(const Solver::Options& options,
bool IsBoundsConstrained(const Program& program) {
const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
for (int i = 0; i < parameter_blocks.size(); ++i) {
if (parameter_blocks[i]->IsConstant()) {
const ParameterBlock* parameter_block = parameter_blocks[i];
if (parameter_block->IsConstant()) {
continue;
}

const double* lower_bounds = parameter_blocks[i]->lower_bounds();
const double* upper_bounds = parameter_blocks[i]->upper_bounds();
const int size = parameter_blocks[i]->Size();
const int size = parameter_block->Size();
for (int j = 0; j < size; ++j) {
if (lower_bounds[j] > -std::numeric_limits<double>::max() ||
upper_bounds[j] < std::numeric_limits<double>::max()) {
const double lower_bound = parameter_block->LowerBoundForParameter(j);
const double upper_bound = parameter_block->UpperBoundForParameter(j);
if (lower_bound > -std::numeric_limits<double>::max() ||
upper_bound < std::numeric_limits<double>::max()) {
return true;
}
}
Expand All @@ -353,24 +353,25 @@ bool ParameterBlocksAreFeasible(const ProblemImpl* problem, string* message) {
const Program& program = problem->program();
const vector<ParameterBlock*>& parameter_blocks = program.parameter_blocks();
for (int i = 0; i < parameter_blocks.size(); ++i) {
const double* array = parameter_blocks[i]->user_state();
const double* lower_bounds = parameter_blocks[i]->lower_bounds();
const double* upper_bounds = parameter_blocks[i]->upper_bounds();
const int size = parameter_blocks[i]->Size();
if (parameter_blocks[i]->IsConstant()) {
const ParameterBlock* parameter_block = parameter_blocks[i];
const double* parameters = parameter_block->user_state();
const int size = parameter_block->Size();
if (parameter_block->IsConstant()) {
// Constant parameter blocks must start in the feasible region
// to ultimately produce a feasible solution, since Ceres cannot
// change them.
for (int j = 0; j < size; ++j) {
if (array[j] < lower_bounds[j] || array[j] > upper_bounds[j]) {
const double lower_bound = parameter_block->LowerBoundForParameter(j);
const double upper_bound = parameter_block->UpperBoundForParameter(j);
if (parameters[j] < lower_bound || parameters[j] > upper_bound) {
*message = StringPrintf(
"ParameterBlock: %p with size %d has at least one infeasible "
"value."
"\nFirst infeasible value is at index: %d."
"\nLower bound: %e, value: %e, upper bound: %e"
"\nParameter block values: ",
array, size, j, lower_bounds[j], array[j], upper_bounds[j]);
AppendArrayToString(size, array, message);
parameters, size, j, lower_bound, parameters[j], upper_bound);
AppendArrayToString(size, parameters, message);
return false;
}
}
Expand All @@ -379,15 +380,17 @@ bool ParameterBlocksAreFeasible(const ProblemImpl* problem, string* message) {
// regions, otherwise there is no way to produce a feasible
// solution.
for (int j = 0; j < size; ++j) {
if (lower_bounds[j] >= upper_bounds[j]) {
const double lower_bound = parameter_block->LowerBoundForParameter(j);
const double upper_bound = parameter_block->UpperBoundForParameter(j);
if (lower_bound >= upper_bound) {
*message = StringPrintf(
"ParameterBlock: %p with size %d has at least one infeasible "
"bound."
"\nFirst infeasible bound is at index: %d."
"\nLower bound: %e, upper bound: %e"
"\nParameter block values: ",
array, size, j, lower_bounds[j], upper_bounds[j]);
AppendArrayToString(size, array, message);
parameters, size, j, lower_bound, upper_bound);
AppendArrayToString(size, parameters, message);
return false;
}
}
Expand Down

0 comments on commit a536ae7

Please sign in to comment.