mirror of
https://github.com/ceres-solver/ceres-solver.git
synced 2026-08-31 01:20:38 +08:00
572ec4a5a5
Objects of 'Expression' can now be freely created and copied around.
This creation does NOT add them to the active ExpressionGraph any
more. The insertion into the graph is now done by ExpressionRef,
which explicitly call graph.add(...) in each operation.
This change brings the following advantages:
1. 'Expression' is now stand-alone and side-effect free
- Remove the dependency Expression->ExpressionGraph
- Expressions can be created by the optimizer
2. Explicit graph insertion
- Previously CreateCompileTimeConstant not only created an
expression, but also inserted it into the active graph. Now
this insertion is done explicitly by ExpressionRef
3. It is now easier to insert new types and members
- Should be straight forward now, because we got rid of the
3-way dependency
- This is a preparation patch for the new return-type member in
https://ceres-solver-review.googlesource.com/c/ceres-solver/+/16224
Change-Id: Icef2fe529a4db001a10d1fb6816c9dc681b14ff2
245 lines
9.8 KiB
C++
245 lines
9.8 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::CreateFunctionCall(#name, {x.id})); \
|
|
}
|
|
#define CERES_DEFINE_BINARY_FUNCTION_CALL(name) \
|
|
inline ExpressionRef name(const ExpressionRef& x, const ExpressionRef& y) { \
|
|
return AddExpressionToGraph( \
|
|
Expression::CreateFunctionCall(#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);
|
|
|
|
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
|