NDEVR
API Documentation
base_binary_edge.h
1#pragma once
2#include "base_edge.h"
3namespace NDEVR
4{
5
6 using namespace Eigen;
7
16 template <sint04 D, typename E, typename VertexXi, typename VertexXj>
17 class BaseBinaryEdge : public BaseEdge<D, E>
18 {
19 public:
20 typedef VertexXi VertexXiType;
21 typedef VertexXj VertexXjType;
22
23 static constexpr int Di = VertexXiType::Dimension;
24 static constexpr int Dj = VertexXjType::Dimension;
25
27 typedef typename Eigen::Matrix<g_type, D, Di>::AlignedMapType JacobianXiOplusType;
28 typedef typename Eigen::Matrix<g_type, D, Dj>::AlignedMapType JacobianXjOplusType;
31
32 typedef Eigen::Map<Eigen::Matrix<g_type, Di, Dj>
33 , Eigen::Matrix<g_type, Di, Dj>::Flags & Eigen::PacketAccessBit
34 ? Eigen::Aligned
35 : Eigen::Unaligned> HessianBlockType;
36 typedef Eigen::Map<Eigen::Matrix<g_type, Dj, Di>
37 , Eigen::Matrix<g_type, Di, Dj>::Flags& Eigen::PacketAccessBit
38 ? Eigen::Aligned
39 : Eigen::Unaligned> HessianBlockTransposedType;
40
43 : BaseEdge<D, E>()
44 // HACK we map to the null pointer for initializing the Maps
47 , _jacobianOplusXi(0, D, Di)
48 , _jacobianOplusXj(0, D, Dj)
49 , _hessianRowMajor(false)
50 {}
51
52 virtual uint04 vertexCount() const final override { return 2; };
55 virtual const HyperGraph::HGVertex* vertex(uint04 i) const final override
56 {
57 if (i == 0)
58 return m_a;
59 else
60 return m_b;
61 };
62
64 virtual HyperGraph::HGVertex* vertex(uint04 i) final override
65 {
66 if (i == 0)
67 return m_a;
68 else
69 return m_b;
70 };
71
74 virtual void setVertex(uint04 i, HyperGraph::HGVertex* v) final override
75 {
76 lib_assert(i < 2, "index out of bounds");
77 if (i == 0)
78 {
79 lib_assert(dynamic_cast<VertexXi*>(v) != nullptr, "Invalid vertex type");
81 }
82 else
83 {
84 lib_assert(dynamic_cast<VertexXj*>(v) != nullptr, "Invalid vertex type");
86 }
87 };
88
93
96 {
97 VertexXiType* from = m_a;
98 VertexXjType* to = m_b;
99
100 const bool fromNotFixed = !from->fixed();
101 const bool toNotFixed = !to->fixed();
102 if (!(fromNotFixed || toNotFixed))
103 return;
104
106 const auto& A = jacobianOplusXi();
107 const auto& B = jacobianOplusXj();
108 const auto At = A.transpose().eval();
109 const auto Bt = B.transpose().eval();
110 const auto omega_error = (omega * _error).eval();
111
112 g_type w_b = 1;
113 InformationType omega_eff;
114 if (this->robustKernel() == nullptr)
115 {
116 omega_eff = omega;
117 }
118 else
119 {
120 const g_type err = this->chi2();
121 EIGEN_ALIGN32 Eigen::Vector3<g_type> rho;
122 this->robustKernel()->robustify(err, rho);
123 w_b = rho[1];
124 omega_eff = this->robustInformation(rho);
125 }
126
127 const auto AtOmega = (At * omega_eff).eval();
128 const auto BtOmega = (Bt * omega_eff).eval();
129
130 if (fromNotFixed)
131 {
132 from->b().noalias() -= At * (omega_error * w_b);
133 from->A().noalias() += AtOmega * A;
134 }
135
136 if (toNotFixed)
137 {
138 to->b().noalias() -= Bt * (omega_error * w_b);
139 to->A().noalias() += BtOmega * B;
140 }
141
142 if (fromNotFixed && toNotFixed)
143 {
144 const auto Hij = (AtOmega * B).eval();
146 _hessianTransposed.noalias() += Hij.transpose();
147 else
148 _hessian.noalias() += Hij;
149 }
150 }
151
153 {
154
155 // get the Jacobian of the nodes in the manifold domain
157 //if (!A.array().isFinite().any())
158 //throw "bad a";
160 //if (!B.array().isFinite().any())
161 //throw "bad a";
162 bool fromNotFixed = !(m_a->fixed());
163 bool toNotFixed = !(m_b->fixed());
164
165 if (fromNotFixed || toNotFixed)
166 {
168 if (this->robustKernel() == nullptr)
169 {
170 if (fromNotFixed)
171 {
172 m_a->b().noalias() += A.transpose() * (-omega * _error);
173 m_a->A().noalias() += (A.transpose() * omega) * A;
174 if (toNotFixed)
175 {
176 if (_hessianRowMajor) // we have to write to the block as transposed
177 _hessianTransposed.noalias() += B.transpose() * (A.transpose() * omega).transpose();
178 else
179 _hessian.noalias() += (A.transpose() * omega) * B;
180 }
181 //if (from->b().array().isNaN().any())
182 // throw "bad a";
183 //if (from->A().array().isNaN().any())
184 // throw "bad a";
185 }
186 if (toNotFixed)
187 {
188 m_b->b().noalias() += B.transpose() * (-omega * _error);
189 m_b->A().noalias() += B.transpose() * omega * B;
190 //if (!to->b().array().isFinite().any())
191 // throw "bad a";
192 //if (!to->A().array().isFinite().any())
193 // throw "bad a";
194 }
195
196 }
197 else
198 { // robust (weighted) error according to some kernel
199 g_type error = this->chi2();
200 EIGEN_ALIGN32 Eigen::Vector3<g_type> rho;
201 this->robustKernel()->robustify(error, rho);
202 EIGEN_ALIGN32 InformationType weightedOmega = this->robustInformation(rho);
203 //std::cout << PVAR(rho.transpose()) << std::endl;
204 //std::cout << PVAR(weightedOmega) << std::endl;
205 auto a_trans = A.transpose();
206 auto b_trans = B.transpose();
207 auto b_weight_omega = weightedOmega * B;
208 if (fromNotFixed)
209 {
210 auto a_weight_omega = weightedOmega * A;
211 m_a->b().noalias() += a_trans * -omega * _error * rho[1];
212 m_a->A().noalias() += a_trans * a_weight_omega;
213 if (toNotFixed)
214 {
215 if (_hessianRowMajor) // we have to write to the block as transposed
216 _hessianTransposed.noalias() += b_trans * a_weight_omega;
217 else
218 _hessian.noalias() += a_trans * b_weight_omega;
219 }
220 //if (from->b().array().isNaN().any())
221 //throw "bad a";
222 //if (from->A().array().isNaN().any())
223 //throw "bad a";
224 }
225 if (toNotFixed)
226 {
227 m_b->b().noalias() += b_trans * -omega * _error * rho[1];
228 //if (to->b().array().isNaN().any())
229 //throw "bad a";
230 m_b->A().noalias() += b_trans * b_weight_omega;
231 //if (to->A().array().isNaN().any())
232 //throw "bad a";
233 }
234
235 }
236 }
237 }
238
239 bool allVerticesFixed() const final override
240 {
241 return m_a->fixed() && m_b->fixed();
242 }
243
244
247 void linearizeOplusAndConstructQuadraticForm(JacobianWorkspace& jacobianWorkspace) final override
248 {
249 lib_assert(jacobianWorkspace.maxNumVertices() >= 2, "Bad vertex size");
250 lib_assert(jacobianWorkspace.maxDimension() >= D * Dj, "Bad vertex dimension");
251 static_assert(sizeof(JacobianXiOplusType) <= D * (1 + Dj) * sizeof(g_type));
252 static_assert(sizeof(JacobianXjOplusType) <= D * (1 + Dj) * sizeof(g_type));
253 new (&_jacobianOplusXi) JacobianXiOplusType(jacobianWorkspace.workspaceForVertex(0), D, Di);
254 new (&_jacobianOplusXj) JacobianXjOplusType(jacobianWorkspace.workspaceForVertex(1), D, Dj);
257 }
258
260 virtual void linearizeOplus()
261 {
262 VertexXiType* vi = m_a;
263 VertexXjType* vj = m_b;
264
265 bool iNotFixed = !(vi->fixed());
266 bool jNotFixed = !(vj->fixed());
267
268 if (!iNotFixed && !jNotFixed)
269 return;
270 const g_type delta = g_type(1e-9);
271 const g_type scalar = g_type(1.0) / (2 * delta);
272 ErrorVector errorBak;
273 ErrorVector errorBeforeNumeric = _error;
274
275 if (iNotFixed)
276 {
277 //Xi - estimate the jacobian numerically
278 g_type add_vi[VertexXiType::Dimension];
279 std::fill(add_vi, add_vi + VertexXiType::Dimension, g_type(0.0));
280 // add small step along the unit vector in each dimension
281 for (int d = 0; d < VertexXiType::Dimension; ++d)
282 {
283 vi->push();
284 add_vi[d] = delta;
285 vi->oplus(add_vi);
286 computeError();
287 errorBak = _error;
288 vi->pop();
289 vi->push();
290 add_vi[d] = -delta;
291 vi->oplus(add_vi);
292 computeError();
293 errorBak -= _error;
294 vi->pop();
295 add_vi[d] = 0.0;
296
297 _jacobianOplusXi.col(d) = scalar * errorBak;
298 } // end dimension
299 }
300
301 if (jNotFixed)
302 {
303 //Xj - estimate the jacobian numerically
304 g_type add_vj[VertexXjType::Dimension];
305 std::fill(add_vj, add_vj + VertexXjType::Dimension, g_type(0.0));
306 // add small step along the unit vector in each dimension
307 for (int d = 0; d < VertexXjType::Dimension; ++d)
308 {
309 vj->push();
310 add_vj[d] = delta;
311 vj->oplus(add_vj);
312 computeError();
313 errorBak = _error;
314 vj->pop();
315 vj->push();
316 add_vj[d] = -delta;
317 vj->oplus(add_vj);
318 computeError();
319 errorBak -= _error;
320 vj->pop();
321 add_vj[d] = 0.0;
322
323 _jacobianOplusXj.col(d) = scalar * errorBak;
324 }
325 } // end dimension
326
327 _error = errorBeforeNumeric;
328 }
329
335 void mapHessianMemory(g_type* d, int i, int j, bool rowMajor) final override
336 {
337 (void)i; (void)j;
338 //assert(i == 0 && j == 1);
339 if (rowMajor) {
340 new (&_hessianTransposed) HessianBlockTransposedType(d, VertexXjType::Dimension, VertexXiType::Dimension);
341 }
342 else {
343 new (&_hessian) HessianBlockType(d, VertexXiType::Dimension, VertexXjType::Dimension);
344 }
345 _hessianRowMajor = rowMajor;
346 }
347 using BaseEdge<D, E>::computeError;
348
349 protected:
350 VertexXi* m_a = nullptr;
351 VertexXj* m_b = nullptr;
352 using BaseEdge<D, E>::_measurement;
353 using BaseEdge<D, E>::_information;
354 using BaseEdge<D, E>::_error;
355
361
362 public:
363 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
364 };
365
366}
virtual void linearizeOplus()
Numerically linearizes the oplus operator by finite differences for both vertices.
const JacobianXjOplusType & jacobianOplusXj() const
returns the result of the linearization in the manifold space for the node xj
Eigen::Matrix< g_type, D, Dj >::AlignedMapType JacobianXjOplusType
Jacobian type w.r.t. vertex Xj.
Eigen::Map< Eigen::Matrix< g_type, Dj, Di >, Eigen::Matrix< g_type, Di, Dj >::Flags &Eigen::PacketAccessBit ? Eigen::Aligned :Eigen::Unaligned > HessianBlockTransposedType
Mapped transposed Hessian block type.
static constexpr int Dj
Dimension of the second vertex.
const JacobianXiOplusType & jacobianOplusXi() const
returns the result of the linearization in the manifold space for the node xi
Eigen::Map< Eigen::Matrix< g_type, Di, Dj >, Eigen::Matrix< g_type, Di, Dj >::Flags &Eigen::PacketAccessBit ? Eigen::Aligned :Eigen::Unaligned > HessianBlockType
Mapped Hessian block type.
static constexpr int Di
Dimension of the first vertex.
void linearizeOplusAndConstructQuadraticForm(JacobianWorkspace &jacobianWorkspace) final override
Linearizes the oplus operator and constructs the quadratic form in one step.
bool allVerticesFixed() const final override
Returns true if both vertices are fixed.
Eigen::Matrix< g_type, D, Di >::AlignedMapType JacobianXiOplusType
Jacobian type w.r.t. vertex Xi.
HessianBlockTransposedType _hessianTransposed
Mapped transposed Hessian block.
bool _hessianRowMajor
Whether the Hessian block is stored in row-major order.
HessianBlockType _hessian
Mapped Hessian block for the off-diagonal (i,j) entry.
virtual void setVertex(uint04 i, HyperGraph::HGVertex *v) final override
Sets vertex i to the given pointer, with type-checking.
virtual const HyperGraph::HGVertex * vertex(uint04 i) const final override
Returns a const pointer to vertex i (0 or 1).
VertexXj * m_b
Pointer to the second (target) vertex.
VertexXi * m_a
Pointer to the first (source) vertex.
JacobianXiOplusType _jacobianOplusXi
Jacobian of the error w.r.t. vertex Xi.
BaseEdge< D, E >::InformationType InformationType
The information matrix type.
BaseEdge< D, E >::Measurement Measurement
The measurement type.
VertexXj VertexXjType
Type of the second (target) vertex.
void mapHessianMemory(g_type *d, int i, int j, bool rowMajor) final override
Maps the Hessian block memory to an external buffer.
JacobianXjOplusType _jacobianOplusXj
Jacobian of the error w.r.t. vertex Xj.
VertexXi VertexXiType
Type of the first (source) vertex.
BaseEdge< D, E >::ErrorVector ErrorVector
The error vector type.
BaseBinaryEdge()
Constructs an empty binary edge with null-mapped Hessian and Jacobian memory.
virtual HyperGraph::HGVertex * vertex(uint04 i) final override
Returns a mutable pointer to vertex i (0 or 1).
virtual uint04 vertexCount() const final override
Returns the number of vertices (always 2 for binary edges).
void constructQuadraticForm2()
Constructs the quadratic form using an optimized evaluation order.
void constructQuadraticForm()
Constructs the quadratic form (Hessian blocks and gradient) from the linearized edge.
Base class for edges with a fixed-size error vector and measurement type.
Definition base_edge.h:17
g_type chi2() const final override
Definition base_edge.h:37
InformationType robustInformation(const Eigen::Vector3< g_type > &rho) const
Definition base_edge.h:81
static constexpr sint04 Dimension
Definition base_edge.h:19
InformationType _information
Definition base_edge.h:74
Eigen::Matrix< g_type, t_dims, t_dims > InformationType
Fixed-size information (inverse covariance) matrix type.
Definition base_edge.h:22
const InformationType & information() const
Returns a const reference to the information matrix.
Definition base_edge.h:50
Measurement _measurement
Definition base_edge.h:75
const ErrorVector & error() const
Definition base_edge.h:43
ErrorVector _error
Definition base_edge.h:76
Eigen::Matrix< g_type, t_dims, 1 > ErrorVector
Fixed-size error vector type.
Definition base_edge.h:21
E Measurement
The measurement type stored by this edge.
Definition base_edge.h:20
abstract Vertex, your types must derive from that one
Definition hyper_graph.h:28
provide memory workspace for computing the Jacobians
virtual void computeError()=0
Computes the error of the edge and stores it internally.
RobustKernel * robustKernel() const
if NOT NULL, error of this edge will be robustifed with the kernel
virtual void robustify(g_type squaredError, Eigen::Vector3< g_type > &rho) const =0
compute the scaling factor for a error: The error is e^T Omega e The output rho is rho[0]: The actual...
The primary namespace for the NDEVR SDK.
uint32_t uint04
-Defines an alias representing a 4 byte, unsigned integer -Can represent exact integer values 0 throu...
constexpr t_to cast(const Angle< t_from > &value)
Casts an Angle from one backing type to another.
Definition Angle.h:408