mirror of
https://github.com/ceres-solver/ceres-solver.git
synced 2026-08-30 00:50:37 +08:00
c8e35e19fd
The generated function names now include the containing namespace. For example: std::abs(...) std::sin(...) ceres::Ternary(...) This patch also fixes the generation of inf/nan compile time constants, using std::numeric_limits. Change-Id: I4a36b09c68dd2adabed49fd4f7f37c8229ab7377
293 lines
9.3 KiB
C++
293 lines
9.3 KiB
C++
// Ceres Solver - A fast non-linear least squares minimizer
|
|
// Copyright 2019 Google Inc. All rights reserved.
|
|
// http://code.google.com/p/ceres-solver/
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
// * Neither the name of Google Inc. nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without
|
|
// specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// Author: darius.rueckert@fau.de (Darius Rueckert)
|
|
|
|
#include "ceres/codegen/internal/code_generator.h"
|
|
|
|
#include <cmath>
|
|
#include <limits>
|
|
#include <sstream>
|
|
|
|
#include "assert.h"
|
|
#include "glog/logging.h"
|
|
namespace ceres {
|
|
namespace internal {
|
|
|
|
CodeGenerator::CodeGenerator(const ExpressionGraph& graph,
|
|
const Options& options)
|
|
: graph_(graph), options_(options) {}
|
|
|
|
std::vector<std::string> CodeGenerator::Generate() {
|
|
std::vector<std::string> code;
|
|
|
|
// 1. Print the header
|
|
if (!options_.function_name.empty()) {
|
|
code.emplace_back(options_.function_name);
|
|
}
|
|
|
|
code.emplace_back("{");
|
|
PushIndentation();
|
|
|
|
// 2. Print declarations
|
|
for (ExpressionId id = 0; id < graph_.Size(); ++id) {
|
|
// By definition of the lhs_id, an expression defines a new variable only if
|
|
// the current_id is identical to the lhs_id.
|
|
const auto& expr = graph_.ExpressionForId(id);
|
|
if (id != expr.lhs_id()) {
|
|
continue;
|
|
}
|
|
//
|
|
// Format: <type> <id>;
|
|
// Example: double v_0;
|
|
//
|
|
const std::string declaration_string =
|
|
indentation_ + ExpressionReturnTypeToString(expr.return_type()) + " " +
|
|
VariableForExpressionId(id) + ";";
|
|
code.emplace_back(declaration_string);
|
|
}
|
|
|
|
// 3. Print code
|
|
for (ExpressionId id = 0; id < graph_.Size(); ++id) {
|
|
code.emplace_back(ExpressionToString(id));
|
|
}
|
|
|
|
PopIndentation();
|
|
CHECK(indentation_.empty()) << "IF - ENDIF missmatch detected.";
|
|
code.emplace_back("}");
|
|
|
|
return code;
|
|
}
|
|
|
|
std::string CodeGenerator::ExpressionToString(ExpressionId id) {
|
|
// An expression is converted into a string, by first adding the required
|
|
// indentation spaces and then adding a ExpressionType-specific string. The
|
|
// following list shows the exact output format for each ExpressionType. The
|
|
// placeholders <value>, <name>,... stand for the respective members value_,
|
|
// name_, ... of the current expression. ExpressionIds such as lhs_id and
|
|
// arguments are converted to the corresponding variable name (7 -> "v_7").
|
|
|
|
auto& expr = graph_.ExpressionForId(id);
|
|
|
|
std::stringstream result;
|
|
result.precision(kFloatingPointPrecision);
|
|
|
|
// Convert the variable names of lhs and arguments to string. This makes the
|
|
// big switch/case below more readable.
|
|
std::string lhs;
|
|
if (expr.HasValidLhs()) {
|
|
lhs = VariableForExpressionId(expr.lhs_id());
|
|
}
|
|
std::vector<std::string> args;
|
|
for (ExpressionId id : expr.arguments()) {
|
|
args.push_back(VariableForExpressionId(id));
|
|
}
|
|
auto value = expr.value();
|
|
const auto& name = expr.name();
|
|
|
|
switch (expr.type()) {
|
|
case ExpressionType::COMPILE_TIME_CONSTANT: {
|
|
//
|
|
// Format: <lhs_id> = <value>;
|
|
// Example: v_0 = 3.1415;
|
|
//
|
|
result << indentation_ << lhs << " = ";
|
|
|
|
// Putting an inf or nan double into std::stringstream will just print the
|
|
// strings "inf" and "nan". This is not valid C++ code so we have to check
|
|
// for this here.
|
|
if (std::isinf(value)) {
|
|
if (value > 0) {
|
|
result << "std::numeric_limits<double>::infinity()";
|
|
} else {
|
|
result << "-std::numeric_limits<double>::infinity()";
|
|
}
|
|
} else if (std::isnan(value)) {
|
|
result << "std::numeric_limits<double>::quiet_NaN()";
|
|
} else {
|
|
result << value;
|
|
}
|
|
result << ";";
|
|
break;
|
|
}
|
|
case ExpressionType::INPUT_ASSIGNMENT: {
|
|
//
|
|
// Format: <lhs_id> = <name>;
|
|
// Example: v_0 = _observed_point_x;
|
|
//
|
|
result << indentation_ << lhs << " = " << name << ";";
|
|
break;
|
|
}
|
|
case ExpressionType::OUTPUT_ASSIGNMENT: {
|
|
//
|
|
// Format: <name> = <arguments[0]>;
|
|
// Example: residual[0] = v_51;
|
|
//
|
|
result << indentation_ << name << " = " << args[0] << ";";
|
|
break;
|
|
}
|
|
case ExpressionType::ASSIGNMENT: {
|
|
//
|
|
// Format: <lhs_id> = <arguments[0]>;
|
|
// Example: v_1 = v_0;
|
|
//
|
|
result << indentation_ << lhs << " = " << args[0] << ";";
|
|
break;
|
|
}
|
|
case ExpressionType::BINARY_ARITHMETIC: {
|
|
//
|
|
// Format: <lhs_id> = <arguments[0]> <name> <arguments[1]>;
|
|
// Example: v_2 = v_0 + v_1;
|
|
//
|
|
result << indentation_ << lhs << " = " << args[0] << " " << name << " "
|
|
<< args[1] << ";";
|
|
break;
|
|
}
|
|
case ExpressionType::UNARY_ARITHMETIC: {
|
|
//
|
|
// Format: <lhs_id> = <name><arguments[0]>;
|
|
// Example: v_1 = -v_0;
|
|
//
|
|
result << indentation_ << lhs << " = " << name << args[0] << ";";
|
|
break;
|
|
}
|
|
case ExpressionType::BINARY_COMPARISON: {
|
|
//
|
|
// Format: <lhs_id> = <arguments[0]> <name> <arguments[1]>;
|
|
// Example: v_2 = v_0 < v_1;
|
|
//
|
|
result << indentation_ << lhs << " = " << args[0] << " " << name << " "
|
|
<< args[1] << ";";
|
|
break;
|
|
}
|
|
case ExpressionType::LOGICAL_NEGATION: {
|
|
//
|
|
// Format: <lhs_id> = !<arguments[0]>;
|
|
// Example: v_1 = !v_0;
|
|
//
|
|
result << indentation_ << lhs << " = !" << args[0] << ";";
|
|
break;
|
|
}
|
|
case ExpressionType::FUNCTION_CALL: {
|
|
//
|
|
// Format: <lhs_id> = <name>(<arguments[0]>, <arguments[1]>, ...);
|
|
// Example: v_1 = sin(v_0);
|
|
//
|
|
result << indentation_ << lhs << " = " << name << "(";
|
|
result << (args.size() ? args[0] : "");
|
|
for (int i = 1; i < args.size(); ++i) {
|
|
result << ", " << args[i];
|
|
}
|
|
result << ");";
|
|
break;
|
|
}
|
|
case ExpressionType::IF: {
|
|
//
|
|
// Format: if (<arguments[0]>) {
|
|
// Example: if (v_0) {
|
|
// Special: Adds 1 level of indentation for all following
|
|
// expressions.
|
|
//
|
|
result << indentation_ << "if (" << args[0] << ") {";
|
|
PushIndentation();
|
|
break;
|
|
}
|
|
case ExpressionType::ELSE: {
|
|
//
|
|
// Format: } else {
|
|
// Example: } else {
|
|
// Special: This expression is printed with one less level of
|
|
// indentation.
|
|
//
|
|
PopIndentation();
|
|
result << indentation_ << "} else {";
|
|
PushIndentation();
|
|
break;
|
|
}
|
|
case ExpressionType::ENDIF: {
|
|
//
|
|
// Format: }
|
|
// Example: }
|
|
// Special: Removes 1 level of indentation for this and all
|
|
// following expressions.
|
|
//
|
|
PopIndentation();
|
|
result << indentation_ << "}";
|
|
break;
|
|
}
|
|
case ExpressionType::COMMENT: {
|
|
//
|
|
// Format: // <name>
|
|
// Example: // this is a comment
|
|
//
|
|
result << indentation_ << "// " + name;
|
|
break;
|
|
}
|
|
case ExpressionType::NOP: {
|
|
//
|
|
// Format: // <NOP>
|
|
// Example: // <NOP>
|
|
//
|
|
result << indentation_ << "// <NOP>";
|
|
break;
|
|
}
|
|
default:
|
|
CHECK(false) << "CodeGenerator::ToString for ExpressionType "
|
|
<< static_cast<int>(expr.type()) << " not implemented!";
|
|
}
|
|
return result.str();
|
|
}
|
|
|
|
std::string CodeGenerator::VariableForExpressionId(ExpressionId id) {
|
|
//
|
|
// Format: <variable_prefix><id>
|
|
// Example: v_42
|
|
//
|
|
auto& expr = graph_.ExpressionForId(id);
|
|
CHECK(expr.lhs_id() == id)
|
|
<< "ExpressionId " << id
|
|
<< " does not have a name (it has not been declared).";
|
|
return options_.variable_prefix + std::to_string(expr.lhs_id());
|
|
}
|
|
|
|
void CodeGenerator::PushIndentation() {
|
|
for (int i = 0; i < options_.indentation_spaces_per_level; ++i) {
|
|
indentation_.push_back(' ');
|
|
}
|
|
}
|
|
|
|
void CodeGenerator::PopIndentation() {
|
|
for (int i = 0; i < options_.indentation_spaces_per_level; ++i) {
|
|
CHECK(!indentation_.empty()) << "IF - ENDIF missmatch detected.";
|
|
indentation_.pop_back();
|
|
}
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace ceres
|