blob: 479344261e70c56d14df0276ccd716d58546cba5 [file] [log] [blame]
Austin Schuh3de38b02024-06-25 18:25:10 -07001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2023 Google Inc. All rights reserved.
3// http://ceres-solver.org/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are met:
7//
8// * Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
12// and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors may be
14// used to endorse or promote products derived from this software without
15// specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27// POSSIBILITY OF SUCH DAMAGE.
28//
29// Author: vitus@google.com (Mike Vitus)
30// jodebo_beck@gmx.de (Johannes Beck)
31
32#ifndef CERES_PUBLIC_INTERNAL_SPHERE_MANIFOLD_HELPERS_H_
33#define CERES_PUBLIC_INTERNAL_SPHERE_MANIFOLD_HELPERS_H_
34
35#include "ceres/constants.h"
36#include "ceres/internal/householder_vector.h"
37
38// This module contains functions to compute the SphereManifold plus and minus
39// operator and their Jacobians.
40//
41// As the parameters to these functions are shared between them, they are
42// described here: The following variable names are used:
43// Plus(x, delta) = x + delta = x_plus_delta,
44// Minus(y, x) = y - x = y_minus_x.
45//
46// The remaining ones are v and beta which describe the Householder
47// transformation of x, and norm_delta which is the norm of delta.
48//
49// The types of x, y, x_plus_delta and y_minus_x need to be equivalent to
50// Eigen::Matrix<double, AmbientSpaceDimension, 1> and the type of delta needs
51// to be equivalent to Eigen::Matrix<double, TangentSpaceDimension, 1>.
52//
53// The type of Jacobian plus needs to be equivalent to Eigen::Matrix<double,
54// AmbientSpaceDimension, TangentSpaceDimension, Eigen::RowMajor> and for
55// Jacobian minus Eigen::Matrix<double, TangentSpaceDimension,
56// AmbientSpaceDimension, Eigen::RowMajor>.
57//
58// For all vector / matrix inputs and outputs, template parameters are
59// used in order to allow also Eigen::Ref and Eigen block expressions to
60// be passed to the function.
61
62namespace ceres::internal {
63
64template <typename VT, typename XT, typename DeltaT, typename XPlusDeltaT>
65inline void ComputeSphereManifoldPlus(const VT& v,
66 double beta,
67 const XT& x,
68 const DeltaT& delta,
69 const double norm_delta,
70 XPlusDeltaT* x_plus_delta) {
71 constexpr int AmbientDim = VT::RowsAtCompileTime;
72
73 // Map the delta from the minimum representation to the over parameterized
74 // homogeneous vector. See B.2 p.25 equation (106) - (107) for more details.
75 const double sin_delta_by_delta = std::sin(norm_delta) / norm_delta;
76
77 Eigen::Matrix<double, AmbientDim, 1> y(v.size());
78 y << sin_delta_by_delta * delta, std::cos(norm_delta);
79
80 // Apply the delta update to remain on the sphere.
81 *x_plus_delta = x.norm() * ApplyHouseholderVector(y, v, beta);
82}
83
84template <typename VT, typename JacobianT>
85inline void ComputeSphereManifoldPlusJacobian(const VT& x,
86 JacobianT* jacobian) {
87 constexpr int AmbientSpaceDim = VT::RowsAtCompileTime;
88 using AmbientVector = Eigen::Matrix<double, AmbientSpaceDim, 1>;
89 const int ambient_size = x.size();
90 const int tangent_size = x.size() - 1;
91
92 AmbientVector v(ambient_size);
93 double beta;
94
95 // NOTE: The explicit template arguments are needed here because
96 // ComputeHouseholderVector is templated and some versions of MSVC
97 // have trouble deducing the type of v automatically.
98 ComputeHouseholderVector<VT, double, AmbientSpaceDim>(x, &v, &beta);
99
100 // The Jacobian is equal to J = H.leftCols(size_ - 1) where H is the
101 // Householder matrix (H = I - beta * v * v').
102 for (int i = 0; i < tangent_size; ++i) {
103 (*jacobian).col(i) = -beta * v(i) * v;
104 (*jacobian)(i, i) += 1.0;
105 }
106 (*jacobian) *= x.norm();
107}
108
109template <typename VT, typename XT, typename YT, typename YMinusXT>
110inline void ComputeSphereManifoldMinus(
111 const VT& v, double beta, const XT& x, const YT& y, YMinusXT* y_minus_x) {
112 constexpr int AmbientSpaceDim = VT::RowsAtCompileTime;
113 constexpr int TangentSpaceDim =
114 AmbientSpaceDim == Eigen::Dynamic ? Eigen::Dynamic : AmbientSpaceDim - 1;
115 using AmbientVector = Eigen::Matrix<double, AmbientSpaceDim, 1>;
116
117 const int tangent_size = v.size() - 1;
118
119 const AmbientVector hy = ApplyHouseholderVector(y, v, beta) / x.norm();
120
121 // Calculate y - x. See B.2 p.25 equation (108).
122 const double y_last = hy[tangent_size];
123 const double hy_norm = hy.template head<TangentSpaceDim>(tangent_size).norm();
124 if (hy_norm == 0.0) {
125 y_minus_x->setZero();
126 y_minus_x->data()[tangent_size - 1] = y_last >= 0 ? 0.0 : constants::pi;
127 } else {
128 *y_minus_x = std::atan2(hy_norm, y_last) / hy_norm *
129 hy.template head<TangentSpaceDim>(tangent_size);
130 }
131}
132
133template <typename VT, typename JacobianT>
134inline void ComputeSphereManifoldMinusJacobian(const VT& x,
135 JacobianT* jacobian) {
136 constexpr int AmbientSpaceDim = VT::RowsAtCompileTime;
137 using AmbientVector = Eigen::Matrix<double, AmbientSpaceDim, 1>;
138 const int ambient_size = x.size();
139 const int tangent_size = x.size() - 1;
140
141 AmbientVector v(ambient_size);
142 double beta;
143
144 // NOTE: The explicit template arguments are needed here because
145 // ComputeHouseholderVector is templated and some versions of MSVC
146 // have trouble deducing the type of v automatically.
147 ComputeHouseholderVector<VT, double, AmbientSpaceDim>(x, &v, &beta);
148
149 // The Jacobian is equal to J = H.leftCols(size_ - 1) where H is the
150 // Householder matrix (H = I - beta * v * v').
151 for (int i = 0; i < tangent_size; ++i) {
152 // NOTE: The transpose is used for correctness (the product is expected to
153 // be a row vector), although here there seems to be no difference between
154 // transposing or not for Eigen (possibly a compile-time auto fix).
155 (*jacobian).row(i) = -beta * v(i) * v.transpose();
156 (*jacobian)(i, i) += 1.0;
157 }
158 (*jacobian) /= x.norm();
159}
160
161} // namespace ceres::internal
162
163#endif