Files
ceres-solver/include/ceres/codegen/internal/expression_ref.h
T
Darius Rueckert 5010421bb7 Add the expression return type as a member to Expression
Before this patch the return type was implicitly defined by the
ExpressionType. This patch separates this connection and allows
each Expression to have one of the predefined types (scalar,
boolean, void).

This patch is required to add support for the functions isfinite,
isinf, isnan, and isnormal. These are function taking a double and
returning a bool.

This also moves some complexity of the code generator to the
Expression, because the generator can direclty get the c++ type.

Change-Id: I8b32bab1bfab2f668875e506d6f3b789a5d1f3fd
2019-12-09 17:13:05 +01:00

259 lines
10 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)
//
// TODO: Documentation
#ifndef CERES_PUBLIC_EXPRESSION_REF_H_
#define CERES_PUBLIC_EXPRESSION_REF_H_
#include <string>
#include "ceres/codegen/internal/expression.h"
#include "ceres/codegen/internal/types.h"
namespace ceres {
namespace internal {
// This class represents a scalar value that creates new expressions during
// evaluation. ExpressionRef can be used as template parameter for cost functors
// and Jets.
//
// ExpressionRef should be passed by value.
struct ExpressionRef {
ExpressionRef() = default;
// Create a compile time constant expression directly from a double value.
// This is important so that we can write T(3.14) in our code and
// it's automatically converted to the correct expression.
//
// This constructor is implicit, because the line
// T a(0);
// must work for T = Jet<ExpressionRef>.
ExpressionRef(double compile_time_constant);
// By adding this deleted constructor we can detect invalid usage of
// ExpressionRef. ExpressionRef must only be created from constexpr doubles.
//
// If you get a compile error here, you have probably written something like:
// T x = local_variable_;
// Change this into:
// T x = CERES_LOCAL_VARIABLE(local_variable_);
ExpressionRef(double&) = delete;
// Copy construction/assignment creates an ASSIGNMENT expression from
// 'other' to 'this'.
//
// For example:
// a = b; // With a.id = 5 and b.id = 3
// will generate the following assignment:
// v_5 = v_3;
//
// If 'this' ExpressionRef is currently not pointing to a variable
// (id==invalid), then an assignment to a new variable is generated. Example:
// T a = 5;
// T b;
// b = a; // During the assignment 'b' is invalid
//
// The right hand side of the assignment (= the argument 'other') must be
// valid in every case. The following code will result in an error.
// T a;
// T b = a; // Error: Uninitialized assignment
ExpressionRef(const ExpressionRef& other);
ExpressionRef& operator=(const ExpressionRef& other);
// Similar to the copy assignment above, but if 'this' is uninitialized, we
// can remove the copy and therefore eliminate one expression in the graph.
// For example:
// T c;
// c = a + b;
// will generate
// v_2 = v_0 + v_1
// instead of an additional assigment from the temporary 'a + b' to 'c'. In
// C++ this concept is called "Copy Elision". This is used by the compiler to
// eliminate copies, for example, in a function that returns an object by
// value. We implement it ourself here, because large parts of copy elision
// are implementation defined, which means that every compiler can do it
// differently. More information on copy elision can be found here:
// https://en.cppreference.com/w/cpp/language/copy_elision
ExpressionRef(ExpressionRef&& other);
ExpressionRef& operator=(ExpressionRef&& other);
// Compound operators
ExpressionRef& operator+=(const ExpressionRef& x);
ExpressionRef& operator-=(const ExpressionRef& x);
ExpressionRef& operator*=(const ExpressionRef& x);
ExpressionRef& operator/=(const ExpressionRef& x);
bool IsInitialized() const { return id != kInvalidExpressionId; }
// The index into the ExpressionGraph data array.
ExpressionId id = kInvalidExpressionId;
static ExpressionRef Create(ExpressionId id);
};
// A helper function which calls 'InsertBack' on the currently active graph.
// This wrapper also checks if StartRecordingExpressions was called. See
// ExpressionGraph::InsertBack for more information.
ExpressionRef AddExpressionToGraph(const Expression& expression);
// Arithmetic Operators
ExpressionRef operator-(const ExpressionRef& x);
ExpressionRef operator+(const ExpressionRef& x);
ExpressionRef operator+(const ExpressionRef& x, const ExpressionRef& y);
ExpressionRef operator-(const ExpressionRef& x, const ExpressionRef& y);
ExpressionRef operator*(const ExpressionRef& x, const ExpressionRef& y);
ExpressionRef operator/(const ExpressionRef& x, const ExpressionRef& y);
// Functions
#define CERES_DEFINE_UNARY_FUNCTION_CALL(name) \
inline ExpressionRef name(const ExpressionRef& x) { \
return AddExpressionToGraph( \
Expression::CreateScalarFunctionCall(#name, {x.id})); \
}
#define CERES_DEFINE_BINARY_FUNCTION_CALL(name) \
inline ExpressionRef name(const ExpressionRef& x, const ExpressionRef& y) { \
return AddExpressionToGraph( \
Expression::CreateScalarFunctionCall(#name, {x.id, y.id})); \
}
CERES_DEFINE_UNARY_FUNCTION_CALL(abs);
CERES_DEFINE_UNARY_FUNCTION_CALL(acos);
CERES_DEFINE_UNARY_FUNCTION_CALL(asin);
CERES_DEFINE_UNARY_FUNCTION_CALL(atan);
CERES_DEFINE_UNARY_FUNCTION_CALL(cbrt);
CERES_DEFINE_UNARY_FUNCTION_CALL(ceil);
CERES_DEFINE_UNARY_FUNCTION_CALL(cos);
CERES_DEFINE_UNARY_FUNCTION_CALL(cosh);
CERES_DEFINE_UNARY_FUNCTION_CALL(exp);
CERES_DEFINE_UNARY_FUNCTION_CALL(exp2);
CERES_DEFINE_UNARY_FUNCTION_CALL(floor);
CERES_DEFINE_UNARY_FUNCTION_CALL(log);
CERES_DEFINE_UNARY_FUNCTION_CALL(log2);
CERES_DEFINE_UNARY_FUNCTION_CALL(sin);
CERES_DEFINE_UNARY_FUNCTION_CALL(sinh);
CERES_DEFINE_UNARY_FUNCTION_CALL(sqrt);
CERES_DEFINE_UNARY_FUNCTION_CALL(tan);
CERES_DEFINE_UNARY_FUNCTION_CALL(tanh);
CERES_DEFINE_BINARY_FUNCTION_CALL(atan2);
CERES_DEFINE_BINARY_FUNCTION_CALL(pow);
#undef CERES_DEFINE_UNARY_FUNCTION_CALL
#undef CERES_DEFINE_BINARY_FUNCTION_CALL
// This additonal type is required, so that we can detect invalid conditions
// during compile time. For example, the following should create a compile time
// error:
//
// ExpressionRef a(5);
// CERES_IF(a){ // Error: Invalid conversion
// ...
//
// Following will work:
//
// ExpressionRef a(5), b(7);
// ComparisonExpressionRef c = a < b;
// CERES_IF(c){
// ...
struct ComparisonExpressionRef {
ExpressionId id;
explicit ComparisonExpressionRef(const ExpressionRef& ref) : id(ref.id) {}
};
ExpressionRef Ternary(const ComparisonExpressionRef& c,
const ExpressionRef& x,
const ExpressionRef& y);
// Comparison operators
ComparisonExpressionRef operator<(const ExpressionRef& x,
const ExpressionRef& y);
ComparisonExpressionRef operator<=(const ExpressionRef& x,
const ExpressionRef& y);
ComparisonExpressionRef operator>(const ExpressionRef& x,
const ExpressionRef& y);
ComparisonExpressionRef operator>=(const ExpressionRef& x,
const ExpressionRef& y);
ComparisonExpressionRef operator==(const ExpressionRef& x,
const ExpressionRef& y);
ComparisonExpressionRef operator!=(const ExpressionRef& x,
const ExpressionRef& y);
// Logical Operators
ComparisonExpressionRef operator&&(const ComparisonExpressionRef& x,
const ComparisonExpressionRef& y);
ComparisonExpressionRef operator||(const ComparisonExpressionRef& x,
const ComparisonExpressionRef& y);
ComparisonExpressionRef operator!(const ComparisonExpressionRef& x);
#define CERES_DEFINE_UNARY_LOGICAL_FUNCTION_CALL(name) \
inline ComparisonExpressionRef name(const ExpressionRef& x) { \
return ComparisonExpressionRef(AddExpressionToGraph( \
Expression::CreateLogicalFunctionCall(#name, {x.id}))); \
}
CERES_DEFINE_UNARY_LOGICAL_FUNCTION_CALL(isfinite);
CERES_DEFINE_UNARY_LOGICAL_FUNCTION_CALL(isinf);
CERES_DEFINE_UNARY_LOGICAL_FUNCTION_CALL(isnan);
CERES_DEFINE_UNARY_LOGICAL_FUNCTION_CALL(isnormal);
#undef CERES_DEFINE_UNARY_LOGICAL_FUNCTION_CALL
template <>
struct InputAssignment<ExpressionRef> {
using ReturnType = ExpressionRef;
static inline ReturnType Get(double /* unused */, const char* name) {
// Note: The scalar value of v will be thrown away, because we don't need it
// during code generation.
return AddExpressionToGraph(Expression::CreateInputAssignment(name));
}
};
template <typename T>
inline typename InputAssignment<T>::ReturnType MakeInputAssignment(
double v, const char* name) {
return InputAssignment<T>::Get(v, name);
}
inline ExpressionRef MakeParameter(const std::string& name) {
return AddExpressionToGraph(Expression::CreateInputAssignment(name));
}
inline ExpressionRef MakeOutput(const ExpressionRef& v,
const std::string& name) {
return AddExpressionToGraph(Expression::CreateOutputAssignment(v.id, name));
}
} // namespace internal
template <>
struct ComparisonReturnType<internal::ExpressionRef> {
using type = internal::ComparisonExpressionRef;
};
} // namespace ceres
#endif