1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722 |
- .. highlight:: c++
- .. default-domain:: cpp
- .. cpp:namespace:: ceres
- .. _`chapter-nnls_modeling`:
- =================================
- Modeling Non-linear Least Squares
- =================================
- Introduction
- ============
- Ceres solver consists of two distinct parts. A modeling API which
- provides a rich set of tools to construct an optimization problem one
- term at a time and a solver API that controls the minimization
- algorithm. This chapter is devoted to the task of modeling
- optimization problems using Ceres. :ref:`chapter-nnls_solving` discusses
- the various ways in which an optimization problem can be solved using
- Ceres.
- Ceres solves robustified bounds constrained non-linear least squares
- problems of the form:
- .. math:: :label: ceresproblem_modeling
- \min_{\mathbf{x}} &\quad \frac{1}{2}\sum_{i}
- \rho_i\left(\left\|f_i\left(x_{i_1},
- ... ,x_{i_k}\right)\right\|^2\right) \\
- \text{s.t.} &\quad l_j \le x_j \le u_j
- In Ceres parlance, the expression
- :math:`\rho_i\left(\left\|f_i\left(x_{i_1},...,x_{i_k}\right)\right\|^2\right)`
- is known as a **residual block**, where :math:`f_i(\cdot)` is a
- :class:`CostFunction` that depends on the **parameter blocks**
- :math:`\left\{x_{i_1},... , x_{i_k}\right\}`.
- In most optimization problems small groups of scalars occur
- together. For example the three components of a translation vector and
- the four components of the quaternion that define the pose of a
- camera. We refer to such a group of scalars as a **parameter block**. Of
- course a parameter block can be just a single scalar too.
- :math:`\rho_i` is a :class:`LossFunction`. A :class:`LossFunction` is
- a scalar valued function that is used to reduce the influence of
- outliers on the solution of non-linear least squares problems.
- :math:`l_j` and :math:`u_j` are lower and upper bounds on the
- parameter block :math:`x_j`.
- As a special case, when :math:`\rho_i(x) = x`, i.e., the identity
- function, and :math:`l_j = -\infty` and :math:`u_j = \infty` we get
- the usual unconstrained `non-linear least squares problem
- <http://en.wikipedia.org/wiki/Non-linear_least_squares>`_.
- .. math:: :label: ceresproblemunconstrained
- \frac{1}{2}\sum_{i} \left\|f_i\left(x_{i_1}, ... ,x_{i_k}\right)\right\|^2.
- :class:`CostFunction`
- =====================
- For each term in the objective function, a :class:`CostFunction` is
- responsible for computing a vector of residuals and Jacobian
- matrices. Concretely, consider a function
- :math:`f\left(x_{1},...,x_{k}\right)` that depends on parameter blocks
- :math:`\left[x_{1}, ... , x_{k}\right]`.
- Then, given :math:`\left[x_{1}, ... , x_{k}\right]`,
- :class:`CostFunction` is responsible for computing the vector
- :math:`f\left(x_{1},...,x_{k}\right)` and the Jacobian matrices
- .. math:: J_i = D_i f(x_1, ..., x_k) \quad \forall i \in \{1, \ldots, k\}
- .. class:: CostFunction
- .. code-block:: c++
- class CostFunction {
- public:
- virtual bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const = 0;
- const std::vector<int32>& parameter_block_sizes();
- int num_residuals() const;
- protected:
- std::vector<int32>* mutable_parameter_block_sizes();
- void set_num_residuals(int num_residuals);
- };
- The signature of the :class:`CostFunction` (number and sizes of input
- parameter blocks and number of outputs) is stored in
- :member:`CostFunction::parameter_block_sizes_` and
- :member:`CostFunction::num_residuals_` respectively. User code
- inheriting from this class is expected to set these two members with
- the corresponding accessors. This information will be verified by the
- :class:`Problem` when added with :func:`Problem::AddResidualBlock`.
- .. function:: bool CostFunction::Evaluate(double const* const* parameters, double* residuals, double** jacobians) const
- Compute the residual vector and the Jacobian matrices.
- ``parameters`` is an array of arrays of size
- ``CostFunction::parameter_block_sizes_.size()`` and
- ``parameters[i]`` is an array of size ``parameter_block_sizes_[i]``
- that contains the :math:`i^{\text{th}}` parameter block that the
- ``CostFunction`` depends on.
- ``parameters`` is never ``nullptr``.
- ``residuals`` is an array of size ``num_residuals_``.
- ``residuals`` is never ``nullptr``.
- ``jacobians`` is an array of arrays of size
- ``CostFunction::parameter_block_sizes_.size()``.
- If ``jacobians`` is ``nullptr``, the user is only expected to compute
- the residuals.
- ``jacobians[i]`` is a row-major array of size ``num_residuals x
- parameter_block_sizes_[i]``.
- If ``jacobians[i]`` is **not** ``nullptr``, the user is required to
- compute the Jacobian of the residual vector with respect to
- ``parameters[i]`` and store it in this array, i.e.
- ``jacobians[i][r * parameter_block_sizes_[i] + c]`` =
- :math:`\frac{\displaystyle \partial \text{residual}[r]}{\displaystyle \partial \text{parameters}[i][c]}`
- If ``jacobians[i]`` is ``nullptr``, then this computation can be
- skipped. This is the case when the corresponding parameter block is
- marked constant.
- The return value indicates whether the computation of the residuals
- and/or jacobians was successful or not. This can be used to
- communicate numerical failures in Jacobian computations for
- instance.
- :class:`SizedCostFunction`
- ==========================
- .. class:: SizedCostFunction
- If the size of the parameter blocks and the size of the residual
- vector is known at compile time (this is the common case),
- :class:`SizeCostFunction` can be used where these values can be
- specified as template parameters and the user only needs to
- implement :func:`CostFunction::Evaluate`.
- .. code-block:: c++
- template<int kNumResiduals, int... Ns>
- class SizedCostFunction : public CostFunction {
- public:
- virtual bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const = 0;
- };
- :class:`AutoDiffCostFunction`
- =============================
- .. class:: AutoDiffCostFunction
- Defining a :class:`CostFunction` or a :class:`SizedCostFunction`
- can be a tedious and error prone especially when computing
- derivatives. To this end Ceres provides `automatic differentiation
- <http://en.wikipedia.org/wiki/Automatic_differentiation>`_.
- .. code-block:: c++
- template <typename CostFunctor,
- int kNumResiduals, // Number of residuals, or ceres::DYNAMIC.
- int... Ns> // Size of each parameter block
- class AutoDiffCostFunction : public
- SizedCostFunction<kNumResiduals, Ns> {
- public:
- AutoDiffCostFunction(CostFunctor* functor, ownership = TAKE_OWNERSHIP);
- // Ignore the template parameter kNumResiduals and use
- // num_residuals instead.
- AutoDiffCostFunction(CostFunctor* functor,
- int num_residuals,
- ownership = TAKE_OWNERSHIP);
- };
- To get an auto differentiated cost function, you must define a
- class with a templated ``operator()`` (a functor) that computes the
- cost function in terms of the template parameter ``T``. The
- autodiff framework substitutes appropriate ``Jet`` objects for
- ``T`` in order to compute the derivative when necessary, but this
- is hidden, and you should write the function as if ``T`` were a
- scalar type (e.g. a double-precision floating point number).
- The function must write the computed value in the last argument
- (the only non-``const`` one) and return true to indicate success.
- For example, consider a scalar error :math:`e = k - x^\top y`,
- where both :math:`x` and :math:`y` are two-dimensional vector
- parameters and :math:`k` is a constant. The form of this error,
- which is the difference between a constant and an expression, is a
- common pattern in least squares problems. For example, the value
- :math:`x^\top y` might be the model expectation for a series of
- measurements, where there is an instance of the cost function for
- each measurement :math:`k`.
- The actual cost added to the total problem is :math:`e^2`, or
- :math:`(k - x^\top y)^2`; however, the squaring is implicitly done
- by the optimization framework.
- To write an auto-differentiable cost function for the above model,
- first define the object
- .. code-block:: c++
- class MyScalarCostFunctor {
- MyScalarCostFunctor(double k): k_(k) {}
- template <typename T>
- bool operator()(const T* const x , const T* const y, T* e) const {
- e[0] = k_ - x[0] * y[0] - x[1] * y[1];
- return true;
- }
- private:
- double k_;
- };
- Note that in the declaration of ``operator()`` the input parameters
- ``x`` and ``y`` come first, and are passed as const pointers to arrays
- of ``T``. If there were three input parameters, then the third input
- parameter would come after ``y``. The output is always the last
- parameter, and is also a pointer to an array. In the example above,
- ``e`` is a scalar, so only ``e[0]`` is set.
- Then given this class definition, the auto differentiated cost
- function for it can be constructed as follows.
- .. code-block:: c++
- CostFunction* cost_function
- = new AutoDiffCostFunction<MyScalarCostFunctor, 1, 2, 2>(
- new MyScalarCostFunctor(1.0)); ^ ^ ^
- | | |
- Dimension of residual ------+ | |
- Dimension of x ----------------+ |
- Dimension of y -------------------+
- In this example, there is usually an instance for each measurement
- of ``k``.
- In the instantiation above, the template parameters following
- ``MyScalarCostFunction``, ``<1, 2, 2>`` describe the functor as
- computing a 1-dimensional output from two arguments, both
- 2-dimensional.
- By default :class:`AutoDiffCostFunction` will take ownership of the cost
- functor pointer passed to it, ie. will call `delete` on the cost functor
- when the :class:`AutoDiffCostFunction` itself is deleted. However, this may
- be undesirable in certain cases, therefore it is also possible to specify
- :class:`DO_NOT_TAKE_OWNERSHIP` as a second argument in the constructor,
- while passing a pointer to a cost functor which does not need to be deleted
- by the AutoDiffCostFunction. For example:
- .. code-block:: c++
- MyScalarCostFunctor functor(1.0)
- CostFunction* cost_function
- = new AutoDiffCostFunction<MyScalarCostFunctor, 1, 2, 2>(
- &functor, DO_NOT_TAKE_OWNERSHIP);
- :class:`AutoDiffCostFunction` also supports cost functions with a
- runtime-determined number of residuals. For example:
- .. code-block:: c++
- CostFunction* cost_function
- = new AutoDiffCostFunction<MyScalarCostFunctor, DYNAMIC, 2, 2>(
- new CostFunctorWithDynamicNumResiduals(1.0), ^ ^ ^
- runtime_number_of_residuals); <----+ | | |
- | | | |
- | | | |
- Actual number of residuals ------+ | | |
- Indicate dynamic number of residuals --------+ | |
- Dimension of x ------------------------------------+ |
- Dimension of y ---------------------------------------+
- .. warning::
- A common beginner's error when first using :class:`AutoDiffCostFunction`
- is to get the sizing wrong. In particular, there is a tendency to set the
- template parameters to (dimension of residual, number of parameters)
- instead of passing a dimension parameter for *every parameter block*. In
- the example above, that would be ``<MyScalarCostFunction, 1, 2>``, which
- is missing the 2 as the last template argument.
- :class:`DynamicAutoDiffCostFunction`
- ====================================
- .. class:: DynamicAutoDiffCostFunction
- :class:`AutoDiffCostFunction` requires that the number of parameter
- blocks and their sizes be known at compile time. In a number of
- applications, this is not enough e.g., Bezier curve fitting, Neural
- Network training etc.
- .. code-block:: c++
- template <typename CostFunctor, int Stride = 4>
- class DynamicAutoDiffCostFunction : public CostFunction {
- };
- In such cases :class:`DynamicAutoDiffCostFunction` can be
- used. Like :class:`AutoDiffCostFunction` the user must define a
- templated functor, but the signature of the functor differs
- slightly. The expected interface for the cost functors is:
- .. code-block:: c++
- struct MyCostFunctor {
- template<typename T>
- bool operator()(T const* const* parameters, T* residuals) const {
- }
- }
- Since the sizing of the parameters is done at runtime, you must
- also specify the sizes after creating the dynamic autodiff cost
- function. For example:
- .. code-block:: c++
- DynamicAutoDiffCostFunction<MyCostFunctor, 4>* cost_function =
- new DynamicAutoDiffCostFunction<MyCostFunctor, 4>(
- new MyCostFunctor());
- cost_function->AddParameterBlock(5);
- cost_function->AddParameterBlock(10);
- cost_function->SetNumResiduals(21);
- Under the hood, the implementation evaluates the cost function
- multiple times, computing a small set of the derivatives (four by
- default, controlled by the ``Stride`` template parameter) with each
- pass. There is a performance tradeoff with the size of the passes;
- Smaller sizes are more cache efficient but result in larger number
- of passes, and larger stride lengths can destroy cache-locality
- while reducing the number of passes over the cost function. The
- optimal value depends on the number and sizes of the various
- parameter blocks.
- As a rule of thumb, try using :class:`AutoDiffCostFunction` before
- you use :class:`DynamicAutoDiffCostFunction`.
- :class:`NumericDiffCostFunction`
- ================================
- .. class:: NumericDiffCostFunction
- In some cases, its not possible to define a templated cost functor,
- for example when the evaluation of the residual involves a call to a
- library function that you do not have control over. In such a
- situation, `numerical differentiation
- <http://en.wikipedia.org/wiki/Numerical_differentiation>`_ can be
- used.
- .. NOTE ::
- TODO(sameeragarwal): Add documentation for the constructor and for
- NumericDiffOptions. Update DynamicNumericDiffOptions in a similar
- manner.
- .. code-block:: c++
- template <typename CostFunctor,
- NumericDiffMethodType method = CENTRAL,
- int kNumResiduals, // Number of residuals, or ceres::DYNAMIC.
- int... Ns> // Size of each parameter block.
- class NumericDiffCostFunction : public
- SizedCostFunction<kNumResiduals, Ns> {
- };
- To get a numerically differentiated :class:`CostFunction`, you must
- define a class with a ``operator()`` (a functor) that computes the
- residuals. The functor must write the computed value in the last
- argument (the only non-``const`` one) and return ``true`` to
- indicate success. Please see :class:`CostFunction` for details on
- how the return value may be used to impose simple constraints on the
- parameter block. e.g., an object of the form
- .. code-block:: c++
- struct ScalarFunctor {
- public:
- bool operator()(const double* const x1,
- const double* const x2,
- double* residuals) const;
- }
- For example, consider a scalar error :math:`e = k - x'y`, where both
- :math:`x` and :math:`y` are two-dimensional column vector
- parameters, the prime sign indicates transposition, and :math:`k` is
- a constant. The form of this error, which is the difference between
- a constant and an expression, is a common pattern in least squares
- problems. For example, the value :math:`x'y` might be the model
- expectation for a series of measurements, where there is an instance
- of the cost function for each measurement :math:`k`.
- To write an numerically-differentiable class:`CostFunction` for the
- above model, first define the object
- .. code-block:: c++
- class MyScalarCostFunctor {
- MyScalarCostFunctor(double k): k_(k) {}
- bool operator()(const double* const x,
- const double* const y,
- double* residuals) const {
- residuals[0] = k_ - x[0] * y[0] + x[1] * y[1];
- return true;
- }
- private:
- double k_;
- };
- Note that in the declaration of ``operator()`` the input parameters
- ``x`` and ``y`` come first, and are passed as const pointers to
- arrays of ``double`` s. If there were three input parameters, then
- the third input parameter would come after ``y``. The output is
- always the last parameter, and is also a pointer to an array. In the
- example above, the residual is a scalar, so only ``residuals[0]`` is
- set.
- Then given this class definition, the numerically differentiated
- :class:`CostFunction` with central differences used for computing
- the derivative can be constructed as follows.
- .. code-block:: c++
- CostFunction* cost_function
- = new NumericDiffCostFunction<MyScalarCostFunctor, CENTRAL, 1, 2, 2>(
- new MyScalarCostFunctor(1.0)); ^ ^ ^ ^
- | | | |
- Finite Differencing Scheme -+ | | |
- Dimension of residual ------------+ | |
- Dimension of x ----------------------+ |
- Dimension of y -------------------------+
- In this example, there is usually an instance for each measurement
- of `k`.
- In the instantiation above, the template parameters following
- ``MyScalarCostFunctor``, ``1, 2, 2``, describe the functor as
- computing a 1-dimensional output from two arguments, both
- 2-dimensional.
- NumericDiffCostFunction also supports cost functions with a
- runtime-determined number of residuals. For example:
- .. code-block:: c++
- CostFunction* cost_function
- = new NumericDiffCostFunction<MyScalarCostFunctor, CENTRAL, DYNAMIC, 2, 2>(
- new CostFunctorWithDynamicNumResiduals(1.0), ^ ^ ^
- TAKE_OWNERSHIP, | | |
- runtime_number_of_residuals); <----+ | | |
- | | | |
- | | | |
- Actual number of residuals ------+ | | |
- Indicate dynamic number of residuals --------------------+ | |
- Dimension of x ------------------------------------------------+ |
- Dimension of y ---------------------------------------------------+
- There are three available numeric differentiation schemes in ceres-solver:
- The ``FORWARD`` difference method, which approximates :math:`f'(x)`
- by computing :math:`\frac{f(x+h)-f(x)}{h}`, computes the cost
- function one additional time at :math:`x+h`. It is the fastest but
- least accurate method.
- The ``CENTRAL`` difference method is more accurate at the cost of
- twice as many function evaluations than forward difference,
- estimating :math:`f'(x)` by computing
- :math:`\frac{f(x+h)-f(x-h)}{2h}`.
- The ``RIDDERS`` difference method[Ridders]_ is an adaptive scheme
- that estimates derivatives by performing multiple central
- differences at varying scales. Specifically, the algorithm starts at
- a certain :math:`h` and as the derivative is estimated, this step
- size decreases. To conserve function evaluations and estimate the
- derivative error, the method performs Richardson extrapolations
- between the tested step sizes. The algorithm exhibits considerably
- higher accuracy, but does so by additional evaluations of the cost
- function.
- Consider using ``CENTRAL`` differences to begin with. Based on the
- results, either try forward difference to improve performance or
- Ridders' method to improve accuracy.
- .. warning::
- A common beginner's error when first using
- :class:`NumericDiffCostFunction` is to get the sizing wrong. In
- particular, there is a tendency to set the template parameters to
- (dimension of residual, number of parameters) instead of passing a
- dimension parameter for *every parameter*. In the example above, that
- would be ``<MyScalarCostFunctor, 1, 2>``, which is missing the last ``2``
- argument. Please be careful when setting the size parameters.
- Numeric Differentiation & Manifolds
- -----------------------------------
- If your cost function depends on a parameter block that must lie on
- a manifold and the functor cannot be evaluated for values of that
- parameter block not on the manifold then you may have problems
- numerically differentiating such functors.
- This is because numeric differentiation in Ceres is performed by
- perturbing the individual coordinates of the parameter blocks that
- a cost functor depends on. This perturbation assumes that the
- parameter block lives on a Euclidean Manifold rather than the
- actual manifold associated with the parameter block. As a result
- some of the perturbed points may not lie on the manifold anymore.
- For example consider a four dimensional parameter block that is
- interpreted as a unit Quaternion. Perturbing the coordinates of
- this parameter block will violate the unit norm property of the
- parameter block.
- Fixing this problem requires that :class:`NumericDiffCostFunction`
- be aware of the :class:`Manifold` associated with each
- parameter block and only generate perturbations in the local
- tangent space of each parameter block.
- For now this is not considered to be a serious enough problem to
- warrant changing the :class:`NumericDiffCostFunction` API. Further,
- in most cases it is relatively straightforward to project a point
- off the manifold back onto the manifold before using it in the
- functor. For example in case of the Quaternion, normalizing the
- 4-vector before using it does the trick.
- **Alternate Interface**
- For a variety of reasons, including compatibility with legacy code,
- :class:`NumericDiffCostFunction` can also take
- :class:`CostFunction` objects as input. The following describes
- how.
- To get a numerically differentiated cost function, define a
- subclass of :class:`CostFunction` such that the
- :func:`CostFunction::Evaluate` function ignores the ``jacobians``
- parameter. The numeric differentiation wrapper will fill in the
- jacobian parameter if necessary by repeatedly calling the
- :func:`CostFunction::Evaluate` with small changes to the
- appropriate parameters, and computing the slope. For performance,
- the numeric differentiation wrapper class is templated on the
- concrete cost function, even though it could be implemented only in
- terms of the :class:`CostFunction` interface.
- The numerically differentiated version of a cost function for a
- cost function can be constructed as follows:
- .. code-block:: c++
- CostFunction* cost_function
- = new NumericDiffCostFunction<MyCostFunction, CENTRAL, 1, 4, 8>(
- new MyCostFunction(...), TAKE_OWNERSHIP);
- where ``MyCostFunction`` has 1 residual and 2 parameter blocks with
- sizes 4 and 8 respectively. Look at the tests for a more detailed
- example.
- :class:`DynamicNumericDiffCostFunction`
- =======================================
- .. class:: DynamicNumericDiffCostFunction
- Like :class:`AutoDiffCostFunction` :class:`NumericDiffCostFunction`
- requires that the number of parameter blocks and their sizes be
- known at compile time. In a number of applications, this is not enough.
- .. code-block:: c++
- template <typename CostFunctor, NumericDiffMethodType method = CENTRAL>
- class DynamicNumericDiffCostFunction : public CostFunction {
- };
- In such cases when numeric differentiation is desired,
- :class:`DynamicNumericDiffCostFunction` can be used.
- Like :class:`NumericDiffCostFunction` the user must define a
- functor, but the signature of the functor differs slightly. The
- expected interface for the cost functors is:
- .. code-block:: c++
- struct MyCostFunctor {
- bool operator()(double const* const* parameters, double* residuals) const {
- }
- }
- Since the sizing of the parameters is done at runtime, you must
- also specify the sizes after creating the dynamic numeric diff cost
- function. For example:
- .. code-block:: c++
- DynamicNumericDiffCostFunction<MyCostFunctor>* cost_function =
- new DynamicNumericDiffCostFunction<MyCostFunctor>(new MyCostFunctor);
- cost_function->AddParameterBlock(5);
- cost_function->AddParameterBlock(10);
- cost_function->SetNumResiduals(21);
- As a rule of thumb, try using :class:`NumericDiffCostFunction` before
- you use :class:`DynamicNumericDiffCostFunction`.
- .. warning::
- The same caution about mixing manifolds with numeric differentiation
- applies as is the case with :class:`NumericDiffCostFunction`.
- :class:`CostFunctionToFunctor`
- ==============================
- .. class:: CostFunctionToFunctor
- :class:`CostFunctionToFunctor` is an adapter class that allows
- users to use :class:`CostFunction` objects in templated functors
- which are to be used for automatic differentiation. This allows
- the user to seamlessly mix analytic, numeric and automatic
- differentiation.
- For example, let us assume that
- .. code-block:: c++
- class IntrinsicProjection : public SizedCostFunction<2, 5, 3> {
- public:
- IntrinsicProjection(const double* observation);
- virtual bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const;
- };
- is a :class:`CostFunction` that implements the projection of a
- point in its local coordinate system onto its image plane and
- subtracts it from the observed point projection. It can compute its
- residual and either via analytic or numerical differentiation can
- compute its jacobians.
- Now we would like to compose the action of this
- :class:`CostFunction` with the action of camera extrinsics, i.e.,
- rotation and translation. Say we have a templated function
- .. code-block:: c++
- template<typename T>
- void RotateAndTranslatePoint(const T* rotation,
- const T* translation,
- const T* point,
- T* result);
- Then we can now do the following,
- .. code-block:: c++
- struct CameraProjection {
- CameraProjection(double* observation)
- : intrinsic_projection_(new IntrinsicProjection(observation)) {
- }
- template <typename T>
- bool operator()(const T* rotation,
- const T* translation,
- const T* intrinsics,
- const T* point,
- T* residual) const {
- T transformed_point[3];
- RotateAndTranslatePoint(rotation, translation, point, transformed_point);
- // Note that we call intrinsic_projection_, just like it was
- // any other templated functor.
- return intrinsic_projection_(intrinsics, transformed_point, residual);
- }
- private:
- CostFunctionToFunctor<2,5,3> intrinsic_projection_;
- };
- Note that :class:`CostFunctionToFunctor` takes ownership of the
- :class:`CostFunction` that was passed in to the constructor.
- In the above example, we assumed that ``IntrinsicProjection`` is a
- ``CostFunction`` capable of evaluating its value and its
- derivatives. Suppose, if that were not the case and
- ``IntrinsicProjection`` was defined as follows:
- .. code-block:: c++
- struct IntrinsicProjection {
- IntrinsicProjection(const double* observation) {
- observation_[0] = observation[0];
- observation_[1] = observation[1];
- }
- bool operator()(const double* calibration,
- const double* point,
- double* residuals) const {
- double projection[2];
- ThirdPartyProjectionFunction(calibration, point, projection);
- residuals[0] = observation_[0] - projection[0];
- residuals[1] = observation_[1] - projection[1];
- return true;
- }
- double observation_[2];
- };
- Here ``ThirdPartyProjectionFunction`` is some third party library
- function that we have no control over. So this function can compute
- its value and we would like to use numeric differentiation to
- compute its derivatives. In this case we can use a combination of
- ``NumericDiffCostFunction`` and ``CostFunctionToFunctor`` to get the
- job done.
- .. code-block:: c++
- struct CameraProjection {
- CameraProjection(double* observation)
- : intrinsic_projection_(
- new NumericDiffCostFunction<IntrinsicProjection, CENTRAL, 2, 5, 3>(
- new IntrinsicProjection(observation))) {}
- template <typename T>
- bool operator()(const T* rotation,
- const T* translation,
- const T* intrinsics,
- const T* point,
- T* residuals) const {
- T transformed_point[3];
- RotateAndTranslatePoint(rotation, translation, point, transformed_point);
- return intrinsic_projection_(intrinsics, transformed_point, residuals);
- }
- private:
- CostFunctionToFunctor<2, 5, 3> intrinsic_projection_;
- };
- :class:`DynamicCostFunctionToFunctor`
- =====================================
- .. class:: DynamicCostFunctionToFunctor
- :class:`DynamicCostFunctionToFunctor` provides the same functionality as
- :class:`CostFunctionToFunctor` for cases where the number and size of the
- parameter vectors and residuals are not known at compile-time. The API
- provided by :class:`DynamicCostFunctionToFunctor` matches what would be
- expected by :class:`DynamicAutoDiffCostFunction`, i.e. it provides a
- templated functor of this form:
- .. code-block:: c++
- template<typename T>
- bool operator()(T const* const* parameters, T* residuals) const;
- Similar to the example given for :class:`CostFunctionToFunctor`, let us
- assume that
- .. code-block:: c++
- class IntrinsicProjection : public CostFunction {
- public:
- IntrinsicProjection(const double* observation);
- virtual bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const;
- };
- is a :class:`CostFunction` that projects a point in its local coordinate
- system onto its image plane and subtracts it from the observed point
- projection.
- Using this :class:`CostFunction` in a templated functor would then look like
- this:
- .. code-block:: c++
- struct CameraProjection {
- CameraProjection(double* observation)
- : intrinsic_projection_(new IntrinsicProjection(observation)) {
- }
- template <typename T>
- bool operator()(T const* const* parameters,
- T* residual) const {
- const T* rotation = parameters[0];
- const T* translation = parameters[1];
- const T* intrinsics = parameters[2];
- const T* point = parameters[3];
- T transformed_point[3];
- RotateAndTranslatePoint(rotation, translation, point, transformed_point);
- const T* projection_parameters[2];
- projection_parameters[0] = intrinsics;
- projection_parameters[1] = transformed_point;
- return intrinsic_projection_(projection_parameters, residual);
- }
- private:
- DynamicCostFunctionToFunctor intrinsic_projection_;
- };
- Like :class:`CostFunctionToFunctor`, :class:`DynamicCostFunctionToFunctor`
- takes ownership of the :class:`CostFunction` that was passed in to the
- constructor.
- :class:`ConditionedCostFunction`
- ================================
- .. class:: ConditionedCostFunction
- This class allows you to apply different conditioning to the residual
- values of a wrapped cost function. An example where this is useful is
- where you have an existing cost function that produces N values, but you
- want the total cost to be something other than just the sum of these
- squared values - maybe you want to apply a different scaling to some
- values, to change their contribution to the cost.
- Usage:
- .. code-block:: c++
- // my_cost_function produces N residuals
- CostFunction* my_cost_function = ...
- CHECK_EQ(N, my_cost_function->num_residuals());
- std::vector<CostFunction*> conditioners;
- // Make N 1x1 cost functions (1 parameter, 1 residual)
- CostFunction* f_1 = ...
- conditioners.push_back(f_1);
- CostFunction* f_N = ...
- conditioners.push_back(f_N);
- ConditionedCostFunction* ccf =
- new ConditionedCostFunction(my_cost_function, conditioners);
- Now ``ccf`` 's ``residual[i]`` (i=0..N-1) will be passed though the
- :math:`i^{\text{th}}` conditioner.
- .. code-block:: c++
- ccf_residual[i] = f_i(my_cost_function_residual[i])
- and the Jacobian will be affected appropriately.
- :class:`GradientChecker`
- ========================
- .. class:: GradientChecker
- This class compares the Jacobians returned by a cost function
- against derivatives estimated using finite differencing. It is
- meant as a tool for unit testing, giving you more fine-grained
- control than the check_gradients option in the solver options.
- The condition enforced is that
- .. math:: \forall{i,j}: \frac{J_{ij} - J'_{ij}}{max_{ij}(J_{ij} - J'_{ij})} < r
- where :math:`J_{ij}` is the jacobian as computed by the supplied
- cost function multiplied by the `Manifold::PlusJacobian`,
- :math:`J'_{ij}` is the jacobian as computed by finite differences,
- multiplied by the `Manifold::PlusJacobian` as well, and :math:`r`
- is the relative precision.
- Usage:
- .. code-block:: c++
- // my_cost_function takes two parameter blocks. The first has a
- // manifold associated with it.
- CostFunction* my_cost_function = ...
- Manifold* my_manifold = ...
- NumericDiffOptions numeric_diff_options;
- std::vector<Manifold*> manifolds;
- manifolds.push_back(my_manifold);
- manifolds.push_back(nullptr);
- std::vector parameter1;
- std::vector parameter2;
- // Fill parameter 1 & 2 with test data...
- std::vector<double*> parameter_blocks;
- parameter_blocks.push_back(parameter1.data());
- parameter_blocks.push_back(parameter2.data());
- GradientChecker gradient_checker(my_cost_function,
- manifolds,
- numeric_diff_options);
- GradientCheckResults results;
- if (!gradient_checker.Probe(parameter_blocks.data(), 1e-9, &results) {
- LOG(ERROR) << "An error has occurred:\n" << results.error_log;
- }
- :class:`NormalPrior`
- ====================
- .. class:: NormalPrior
- .. code-block:: c++
- class NormalPrior: public CostFunction {
- public:
- // Check that the number of rows in the vector b are the same as the
- // number of columns in the matrix A, crash otherwise.
- NormalPrior(const Matrix& A, const Vector& b);
- virtual bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const;
- };
- Implements a cost function of the form
- .. math:: cost(x) = ||A(x - b)||^2
- where, the matrix :math:`A` and the vector :math:`b` are fixed and :math:`x`
- is the variable. In case the user is interested in implementing a cost
- function of the form
- .. math:: cost(x) = (x - \mu)^T S^{-1} (x - \mu)
- where, :math:`\mu` is a vector and :math:`S` is a covariance matrix,
- then, :math:`A = S^{-1/2}`, i.e the matrix :math:`A` is the square
- root of the inverse of the covariance, also known as the stiffness
- matrix. There are however no restrictions on the shape of
- :math:`A`. It is free to be rectangular, which would be the case if
- the covariance matrix :math:`S` is rank deficient.
- .. _`section-loss_function`:
- :class:`LossFunction`
- =====================
- .. class:: LossFunction
- For least squares problems where the minimization may encounter
- input terms that contain outliers, that is, completely bogus
- measurements, it is important to use a loss function that reduces
- their influence.
- Consider a structure from motion problem. The unknowns are 3D
- points and camera parameters, and the measurements are image
- coordinates describing the expected reprojected position for a
- point in a camera. For example, we want to model the geometry of a
- street scene with fire hydrants and cars, observed by a moving
- camera with unknown parameters, and the only 3D points we care
- about are the pointy tippy-tops of the fire hydrants. Our magic
- image processing algorithm, which is responsible for producing the
- measurements that are input to Ceres, has found and matched all
- such tippy-tops in all image frames, except that in one of the
- frame it mistook a car's headlight for a hydrant. If we didn't do
- anything special the residual for the erroneous measurement will
- result in the entire solution getting pulled away from the optimum
- to reduce the large error that would otherwise be attributed to the
- wrong measurement.
- Using a robust loss function, the cost for large residuals is
- reduced. In the example above, this leads to outlier terms getting
- down-weighted so they do not overly influence the final solution.
- .. code-block:: c++
- class LossFunction {
- public:
- virtual void Evaluate(double s, double out[3]) const = 0;
- };
- The key method is :func:`LossFunction::Evaluate`, which given a
- non-negative scalar ``s``, computes
- .. math:: out = \begin{bmatrix}\rho(s), & \rho'(s), & \rho''(s)\end{bmatrix}
- Here the convention is that the contribution of a term to the cost
- function is given by :math:`\frac{1}{2}\rho(s)`, where :math:`s
- =\|f_i\|^2`. Calling the method with a negative value of :math:`s`
- is an error and the implementations are not required to handle that
- case.
- Most sane choices of :math:`\rho` satisfy:
- .. math::
- \rho(0) &= 0\\
- \rho'(0) &= 1\\
- \rho'(s) &< 1 \text{ in the outlier region}\\
- \rho''(s) &< 0 \text{ in the outlier region}
- so that they mimic the squared cost for small residuals.
- **Scaling**
- Given one robustifier :math:`\rho(s)` one can change the length
- scale at which robustification takes place, by adding a scale
- factor :math:`a > 0` which gives us :math:`\rho(s,a) = a^2 \rho(s /
- a^2)` and the first and second derivatives as :math:`\rho'(s /
- a^2)` and :math:`(1 / a^2) \rho''(s / a^2)` respectively.
- The reason for the appearance of squaring is that :math:`a` is in
- the units of the residual vector norm whereas :math:`s` is a squared
- norm. For applications it is more convenient to specify :math:`a` than
- its square.
- Instances
- ---------
- Ceres includes a number of predefined loss functions. For simplicity
- we described their unscaled versions. The figure below illustrates
- their shape graphically. More details can be found in
- ``include/ceres/loss_function.h``.
- .. figure:: loss.png
- :figwidth: 500px
- :height: 400px
- :align: center
- Shape of the various common loss functions.
- .. class:: TrivialLoss
- .. math:: \rho(s) = s
- .. class:: HuberLoss
- .. math:: \rho(s) = \begin{cases} s & s \le 1\\ 2 \sqrt{s} - 1 & s > 1 \end{cases}
- .. class:: SoftLOneLoss
- .. math:: \rho(s) = 2 (\sqrt{1+s} - 1)
- .. class:: CauchyLoss
- .. math:: \rho(s) = \log(1 + s)
- .. class:: ArctanLoss
- .. math:: \rho(s) = \arctan(s)
- .. class:: TolerantLoss
- .. math:: \rho(s,a,b) = b \log(1 + e^{(s - a) / b}) - b \log(1 + e^{-a / b})
- .. class:: TukeyLoss
- .. math:: \rho(s) = \begin{cases} \frac{1}{3} (1 - (1 - s)^3) & s \le 1\\ \frac{1}{3} & s > 1 \end{cases}
- .. class:: ComposedLoss
- Given two loss functions ``f`` and ``g``, implements the loss
- function ``h(s) = f(g(s))``.
- .. code-block:: c++
- class ComposedLoss : public LossFunction {
- public:
- explicit ComposedLoss(const LossFunction* f,
- Ownership ownership_f,
- const LossFunction* g,
- Ownership ownership_g);
- };
- .. class:: ScaledLoss
- Sometimes you want to simply scale the output value of the
- robustifier. For example, you might want to weight different error
- terms differently (e.g., weight pixel reprojection errors
- differently from terrain errors).
- Given a loss function :math:`\rho(s)` and a scalar :math:`a`, :class:`ScaledLoss`
- implements the function :math:`a \rho(s)`.
- Since we treat a ``nullptr`` Loss function as the Identity loss
- function, :math:`rho` = ``nullptr``: is a valid input and will result
- in the input being scaled by :math:`a`. This provides a simple way
- of implementing a scaled ResidualBlock.
- .. class:: LossFunctionWrapper
- Sometimes after the optimization problem has been constructed, we
- wish to mutate the scale of the loss function. For example, when
- performing estimation from data which has substantial outliers,
- convergence can be improved by starting out with a large scale,
- optimizing the problem and then reducing the scale. This can have
- better convergence behavior than just using a loss function with a
- small scale.
- This templated class allows the user to implement a loss function
- whose scale can be mutated after an optimization problem has been
- constructed, e.g,
- .. code-block:: c++
- Problem problem;
- // Add parameter blocks
- CostFunction* cost_function =
- new AutoDiffCostFunction < UW_Camera_Mapper, 2, 9, 3>(
- new UW_Camera_Mapper(feature_x, feature_y));
- LossFunctionWrapper* loss_function(new HuberLoss(1.0), TAKE_OWNERSHIP);
- problem.AddResidualBlock(cost_function, loss_function, parameters);
- Solver::Options options;
- Solver::Summary summary;
- Solve(options, &problem, &summary);
- loss_function->Reset(new HuberLoss(1.0), TAKE_OWNERSHIP);
- Solve(options, &problem, &summary);
- Theory
- ------
- Let us consider a problem with a single parameter block.
- .. math::
- \min_x \frac{1}{2}\rho(f^2(x))
- Then, the robustified gradient and the Gauss-Newton Hessian are
- .. math::
- g(x) &= \rho'J^\top(x)f(x)\\
- H(x) &= J^\top(x)\left(\rho' + 2 \rho''f(x)f^\top(x)\right)J(x)
- where the terms involving the second derivatives of :math:`f(x)` have
- been ignored. Note that :math:`H(x)` is indefinite if
- :math:`\rho''f(x)^\top f(x) + \frac{1}{2}\rho' < 0`. If this is not
- the case, then its possible to re-weight the residual and the Jacobian
- matrix such that the robustified Gauss-Newton step corresponds to an
- ordinary linear least squares problem.
- Let :math:`\alpha` be a root of
- .. math:: \frac{1}{2}\alpha^2 - \alpha - \frac{\rho''}{\rho'}\|f(x)\|^2 = 0.
- Then, define the rescaled residual and Jacobian as
- .. math::
- \tilde{f}(x) &= \frac{\sqrt{\rho'}}{1 - \alpha} f(x)\\
- \tilde{J}(x) &= \sqrt{\rho'}\left(1 - \alpha
- \frac{f(x)f^\top(x)}{\left\|f(x)\right\|^2} \right)J(x)
- In the case :math:`2 \rho''\left\|f(x)\right\|^2 + \rho' \lesssim 0`,
- we limit :math:`\alpha \le 1- \epsilon` for some small
- :math:`\epsilon`. For more details see [Triggs]_.
- With this simple rescaling, one can apply any Jacobian based non-linear
- least squares algorithm to robustified non-linear least squares
- problems.
- While the theory described above is elegant, in practice we observe
- that using the Triggs correction when :math:`\rho'' > 0` leads to poor
- performance, so we upper bound it by zero. For more details see
- `corrector.cc <https://github.com/ceres-solver/ceres-solver/blob/master/internal/ceres/corrector.cc#L51>`_
- :class:`Manifold`
- ==================
- .. class:: Manifold
- In sensor fusion problems, we often have to model quantities that live
- in spaces known as `Manifolds
- <https://en.wikipedia.org/wiki/Manifold>`_, for example the
- rotation/orientation of a sensor that is represented by a `Quaternion
- <https://en.wikipedia.org/wiki/Quaternion>`_.
- Manifolds are spaces which locally look like Euclidean spaces. More
- precisely, at each point on the manifold there is a linear space that
- is tangent to the manifold. It has dimension equal to the intrinsic
- dimension of the manifold itself, which is less than or equal to the
- ambient space in which the manifold is embedded.
- For example, the tangent space to a point on a sphere in three
- dimensions is the two dimensional plane that is tangent to the sphere
- at that point. There are two reasons tangent spaces are interesting:
- 1. They are Eucliean spaces so the usual vector space operations apply
- there, which makes numerical operations easy.
- 2. Movements in the tangent space translate into movements along the
- manifold. Movements perpendicular to the tangent space do not
- translate into movements on the manifold.
- However, moving along the 2 dimensional plane tangent to the sphere
- and projecting back onto the sphere will move you away from the point
- you started from but moving along the normal at the same point and the
- projecting back onto the sphere brings you back to the point.
- Besides the mathematical niceness, modeling manifold valued
- quantities correctly and paying attention to their geometry has
- practical benefits too:
- 1. It naturally constrains the quantity to the manifold throughout the
- optimization, freeing the user from hacks like *quaternion
- normalization*.
- 2. It reduces the dimension of the optimization problem to its
- *natural* size. For example, a quantity restricted to a line is a
- one dimensional object regardless of the dimension of the ambient
- space in which this line lives.
- Working in the tangent space reduces not just the computational
- complexity of the optimization algorithm, but also improves the
- numerical behaviour of the algorithm.
- A basic operation one can perform on a manifold is the
- :math:`\boxplus` operation that computes the result of moving along
- :math:`\delta` in the tangent space at :math:`x`, and then projecting
- back onto the manifold that :math:`x` belongs to. Also known as a
- *Retraction*, :math:`\boxplus` is a generalization of vector addition
- in Euclidean spaces.
- The inverse of :math:`\boxplus` is :math:`\boxminus`, which given two
- points :math:`y` and :math:`x` on the manifold computes the tangent
- vector :math:`\Delta` at :math:`x` s.t. :math:`\boxplus(x, \Delta) =
- y`.
- Let us now consider two examples.
- The `Euclidean space <https://en.wikipedia.org/wiki/Euclidean_space>`_
- :math:`\mathbb{R}^n` is the simplest example of a manifold. It has
- dimension :math:`n` (and so does its tangent space) and
- :math:`\boxplus` and :math:`\boxminus` are the familiar vector sum and
- difference operations.
- .. math::
- \begin{align*}
- \boxplus(x, \Delta) &= x + \Delta = y\\
- \boxminus(y, x) &= y - x = \Delta.
- \end{align*}
- A more interesting case is the case :math:`SO(3)`, the `special
- orthogonal group <https://en.wikipedia.org/wiki/3D_rotation_group>`_
- in three dimensions - the space of :math:`3\times3` rotation
- matrices. :math:`SO(3)` is a three dimensional manifold embedded in
- :math:`\mathbb{R}^9` or :math:`\mathbb{R}^{3\times 3}`. So points on :math:`SO(3)` are
- represented using 9 dimensional vectors or :math:`3\times 3` matrices,
- and points in its tangent spaces are represented by 3 dimensional
- vectors.
- For :math:`SO(3)`, :math:`\boxplus` and :math:`\boxminus` are defined
- in terms of the matrix :math:`\exp` and :math:`\log` operations as
- follows:
- Given a 3-vector :math:`\Delta = [\begin{matrix}p,&q,&r\end{matrix}]`, we have
- .. math::
- \exp(\Delta) & = \left [ \begin{matrix}
- \cos \theta + cp^2 & -sr + cpq & sq + cpr \\
- sr + cpq & \cos \theta + cq^2& -sp + cqr \\
- -sq + cpr & sp + cqr & \cos \theta + cr^2
- \end{matrix} \right ]
- where,
- .. math::
- \begin{align}
- \theta &= \sqrt{p^2 + q^2 + r^2},\\
- s &= \frac{\sin \theta}{\theta},\\
- c &= \frac{1 - \cos \theta}{\theta^2}.
- \end{align}
- Given :math:`x \in SO(3)`, we have
- .. math::
- \log(x) = 1/(2 \sin(\theta)/\theta)\left[\begin{matrix} x_{32} - x_{23},& x_{13} - x_{31},& x_{21} - x_{12}\end{matrix} \right]
- where,
- .. math:: \theta = \cos^{-1}((\operatorname{Trace}(x) - 1)/2)
- Then,
- .. math::
- \begin{align*}
- \boxplus(x, \Delta) &= x \exp(\Delta)
- \\
- \boxminus(y, x) &= \log(x^T y)
- \end{align*}
- For :math:`\boxplus` and :math:`\boxminus` to be mathematically
- consistent, the following identities must be satisfied at all points
- :math:`x` on the manifold:
- 1. :math:`\boxplus(x, 0) = x`. This ensures that the tangent space is
- *centered* at :math:`x`, and the zero vector is the identity
- element.
- 2. For all :math:`y` on the manifold, :math:`\boxplus(x,
- \boxminus(y,x)) = y`. This ensures that any :math:`y` can be
- reached from math:`x`.
- 3. For all :math:`\Delta`, :math:`\boxminus(\boxplus(x, \Delta), x) =
- \Delta`. This ensures that :math:`\boxplus` is an injective
- (one-to-one) map.
- 4. For all :math:`\Delta_1, \Delta_2\ |\boxminus(\boxplus(x, \Delta_1),
- \boxplus(x, \Delta_2)) \leq |\Delta_1 - \Delta_2|`. Allows us to define
- a metric on the manifold.
- Additionally we require that :math:`\boxplus` and :math:`\boxminus` be
- sufficiently smooth. In particular they need to be differentiable
- everywhere on the manifold.
- For more details, please see [Hertzberg]_
- The :class:`Manifold` interface allows the user to define a manifold
- for the purposes optimization by implementing ``Plus`` and ``Minus``
- operations and their derivatives (corresponding naturally to
- :math:`\boxplus` and :math:`\boxminus`).
- .. code-block:: c++
- class Manifold {
- public:
- virtual ~Manifold();
- virtual int AmbientSize() const = 0;
- virtual int TangentSize() const = 0;
- virtual bool Plus(const double* x,
- const double* delta,
- double* x_plus_delta) const = 0;
- virtual bool PlusJacobian(const double* x, double* jacobian) const = 0;
- virtual bool RightMultiplyByPlusJacobian(const double* x,
- const int num_rows,
- const double* ambient_matrix,
- double* tangent_matrix) const;
- virtual bool Minus(const double* y,
- const double* x,
- double* y_minus_x) const = 0;
- virtual bool MinusJacobian(const double* x, double* jacobian) const = 0;
- };
- .. function:: int Manifold::AmbientSize() const;
- Dimension of the ambient space in which the manifold is embedded.
- .. function:: int Manifold::TangentSize() const;
- Dimension of the manifold/tangent space.
- .. function:: bool Plus(const double* x, const double* delta, double* x_plus_delta) const;
- Implements the :math:`\boxplus(x,\Delta)` operation for the manifold.
- A generalization of vector addition in Euclidean space, ``Plus``
- computes the result of moving along ``delta`` in the tangent space
- at ``x``, and then projecting back onto the manifold that ``x``
- belongs to.
- ``x`` and ``x_plus_delta`` are :func:`Manifold::AmbientSize` vectors.
- ``delta`` is a :func:`Manifold::TangentSize` vector.
- Return value indicates if the operation was successful or not.
- .. function:: bool PlusJacobian(const double* x, double* jacobian) const;
- Compute the derivative of :math:`\boxplus(x, \Delta)` w.r.t
- :math:`\Delta` at :math:`\Delta = 0`, i.e. :math:`(D_2
- \boxplus)(x, 0)`.
- ``jacobian`` is a row-major :func:`Manifold::AmbientSize`
- :math:`\times` :func:`Manifold::TangentSize` matrix.
- Return value indicates whether the operation was successful or not.
- .. function:: bool RightMultiplyByPlusJacobian(const double* x, const int num_rows, const double* ambient_matrix, double* tangent_matrix) const;
- ``tangent_matrix`` = ``ambient_matrix`` :math:`\times` plus_jacobian.
- ``ambient_matrix`` is a row-major ``num_rows`` :math:`\times`
- :func:`Manifold::AmbientSize` matrix.
- ``tangent_matrix`` is a row-major ``num_rows`` :math:`\times`
- :func:`Manifold::TangentSize` matrix.
- Return value indicates whether the operation was successful or not.
- This function is only used by the :class:`GradientProblemSolver`,
- where the dimension of the parameter block can be large and it may
- be more efficient to compute this product directly rather than
- first evaluating the Jacobian into a matrix and then doing a matrix
- vector product.
- Because this is not an often used function, we provide a default
- implementation for convenience. If performance becomes an issue
- then the user should consider implementing a specialization.
- .. function:: bool Minus(const double* y, const double* x, double* y_minus_x) const;
- Implements :math:`\boxminus(y,x)` operation for the manifold.
- A generalization of vector subtraction in Euclidean spaces, given
- two points ``x`` and ``y`` on the manifold, ``Minus`` computes the
- change to ``x`` in the tangent space at ``x``, that will take it to
- ``y``.
- ``x`` and ``y`` are :func:`Manifold::AmbientSize` vectors.
- ``y_minus_x`` is a ::func:`Manifold::TangentSize` vector.
- Return value indicates if the operation was successful or not.
- .. function:: bool MinusJacobian(const double* x, double* jacobian) const = 0;
- Compute the derivative of :math:`\boxminus(y, x)` w.r.t :math:`y`
- at :math:`y = x`, i.e :math:`(D_1 \boxminus) (x, x)`.
- ``jacobian`` is a row-major :func:`Manifold::TangentSize`
- :math:`\times` :func:`Manifold::AmbientSize` matrix.
- Return value indicates whether the operation was successful or not.
- Ceres Solver ships with a number of commonly used instances of
- :class:`Manifold`.
- For `Lie Groups <https://en.wikipedia.org/wiki/Lie_group>`_, a great
- place to find high quality implementations is the `Sophus
- <https://github.com/strasdat/Sophus>`_ library developed by Hauke
- Strasdat and his collaborators.
- :class:`EuclideanManifold`
- --------------------------
- .. class:: EuclideanManifold
- :class:`EuclideanManifold` as the name implies represents a Euclidean
- space, where the :math:`\boxplus` and :math:`\boxminus` operations are
- the usual vector addition and subtraction.
- .. math::
- \begin{align*}
- \boxplus(x, \Delta) &= x + \Delta\\
- \boxminus(y,x) &= y - x
- \end{align*}
- By default parameter blocks are assumed to be Euclidean, so there is
- no need to use this manifold on its own. It is provided for the
- purpose of testing and for use in combination with other manifolds
- using :class:`ProductManifold`.
- The class works with dynamic and static ambient space dimensions. If
- the ambient space dimensions is known at compile time use
- .. code-block:: c++
- EuclideanManifold<3> manifold;
- If the ambient space dimensions is not known at compile time the
- template parameter needs to be set to `ceres::DYNAMIC` and the actual
- dimension needs to be provided as a constructor argument:
- .. code-block:: c++
- EuclideanManifold<ceres::DYNAMIC> manifold(ambient_dim);
- :class:`SubsetManifold`
- -----------------------
- .. class:: SubsetManifold
- Suppose :math:`x` is a two dimensional vector, and the user wishes to
- hold the first coordinate constant. Then, :math:`\Delta` is a scalar
- and :math:`\boxplus` is defined as
- .. math::
- \boxplus(x, \Delta) = x + \left[ \begin{array}{c} 0 \\ 1 \end{array} \right] \Delta
- and given two, two-dimensional vectors :math:`x` and :math:`y` with
- the same first coordinate, :math:`\boxminus` is defined as:
- .. math::
- \boxminus(y, x) = y[1] - x[1]
- :class:`SubsetManifold` generalizes this construction to hold
- any part of a parameter block constant by specifying the set of
- coordinates that are held constant.
- .. NOTE::
- It is legal to hold *all* coordinates of a parameter block to
- constant using a :class:`SubsetManifold`. It is the same as calling
- :func:`Problem::SetParameterBlockConstant` on that parameter block.
- :class:`ProductManifold`
- ------------------------
- .. class:: ProductManifold
- In cases, where a parameter block is the Cartesian product of a number
- of manifolds and you have the manifold of the individual
- parameter blocks available, :class:`ProductManifold` can be used to
- construct a :class:`Manifold` of the Cartesian product.
- For the case of the rigid transformation, where say you have a
- parameter block of size 7, where the first four entries represent the
- rotation as a quaternion, and the next three the translation, a
- manifold can be constructed as:
- .. code-block:: c++
- ProductManifold<QuaternionManifold, EuclideanManifold<3>> se3;
- Manifolds can be copied and moved to :class:`ProductManifold`:
- .. code-block:: c++
- SubsetManifold manifold1(5, {2});
- SubsetManifold manifold2(3, {0, 1});
- ProductManifold<SubsetManifold, SubsetManifold> manifold(manifold1,
- manifold2);
- In advanced use cases, manifolds can be dynamically allocated and passed as (smart) pointers:
- .. code-block:: c++
- ProductManifold<std::unique_ptr<QuaternionManifold>, EuclideanManifold<3>> se3
- {std::make_unique<QuaternionManifold>(), EuclideanManifold<3>{}};
- In C++17, the template parameters can be left out as they are automatically
- deduced making the initialization much simpler:
- .. code-block:: c++
- ProductManifold se3{QuaternionManifold{}, EuclideanManifold<3>{}};
- :class:`QuaternionManifold`
- ---------------------------
- .. class:: QuaternionManifold
- .. NOTE::
- If you are using ``Eigen`` quaternions, then you should use
- :class:`EigenQuaternionManifold` instead because ``Eigen`` uses a
- different memory layout for its Quaternions.
- Manifold for a Hamilton `Quaternion
- <https://en.wikipedia.org/wiki/Quaternion>`_. Quaternions are a three
- dimensional manifold represented as unit norm 4-vectors, i.e.
- .. math:: q = \left [\begin{matrix}q_0,& q_1,& q_2,& q_3\end{matrix}\right], \quad \|q\| = 1
- is the ambient space representation. Here :math:`q_0` is the scalar
- part. :math:`q_1` is the coefficient of :math:`i`, :math:`q_2` is the
- coefficient of :math:`j`, and :math:`q_3` is the coefficient of
- :math:`k`. Where:
- .. math::
- \begin{align*}
- i\times j &= k,\\
- j\times k &= i,\\
- k\times i &= j,\\
- i\times i &= -1,\\
- j\times j &= -1,\\
- k\times k &= -1.
- \end{align*}
- The tangent space is three dimensional and the :math:`\boxplus` and
- :math:`\boxminus` operators are defined in term of :math:`\exp` and
- :math:`\log` operations.
- .. math::
- \begin{align*}
- \boxplus(x, \Delta) &= \exp\left(\Delta\right) \otimes x \\
- \boxminus(y,x) &= \log\left(y \otimes x^{-1}\right)
- \end{align*}
- Where :math:`\otimes` is the `Quaternion product
- <https://en.wikipedia.org/wiki/Quaternion#Hamilton_product>`_ and
- since :math:`x` is a unit quaternion, :math:`x^{-1} = [\begin{matrix}
- q_0,& -q_1,& -q_2,& -q_3\end{matrix}]`. Given a vector :math:`\Delta
- \in \mathbb{R}^3`,
- .. math::
- \exp(\Delta) = \left[ \begin{matrix}
- \cos\left(\|\Delta\|\right)\\
- \frac{\displaystyle \sin\left(|\Delta\|\right)}{\displaystyle \|\Delta\|} \Delta
- \end{matrix} \right]
- and given a unit quaternion :math:`q = \left [\begin{matrix}q_0,& q_1,& q_2,& q_3\end{matrix}\right]`
- .. math::
- \log(q) = \frac{\operatorname{atan2}\left(\sqrt{1-q_0^2},q_0\right)}{\sqrt{1-q_0^2}} \left [\begin{matrix}q_1,& q_2,& q_3\end{matrix}\right]
- :class:`EigenQuaternionManifold`
- --------------------------------
- .. class:: EigenQuaternionManifold
- Implements the quaternion manifold for `Eigen's
- <http://eigen.tuxfamily.org/index.php?title=Main_Page>`_
- representation of the Hamilton quaternion. Geometrically it is exactly
- the same as the :class:`QuaternionManifold` defined above. However,
- Eigen uses a different internal memory layout for the elements of the
- quaternion than what is commonly used. It stores the quaternion in
- memory as :math:`[q_1, q_2, q_3, q_0]` or :math:`[x, y, z, w]` where
- the real (scalar) part is last.
- Since Ceres operates on parameter blocks which are raw double pointers
- this difference is important and requires a different manifold.
- :class:`SphereManifold`
- -----------------------
- .. class:: SphereManifold
- This provides a manifold on a sphere meaning that the norm of the
- vector stays the same. Such cases often arises in Structure for Motion
- problems. One example where they are used is in representing points
- whose triangulation is ill-conditioned. Here it is advantageous to use
- an over-parameterization since homogeneous vectors can represent
- points at infinity.
- The ambient space dimension is required to be greater than 1.
- The class works with dynamic and static ambient space dimensions. If
- the ambient space dimensions is known at compile time use
- .. code-block:: c++
- SphereManifold<3> manifold;
- If the ambient space dimensions is not known at compile time the
- template parameter needs to be set to `ceres::DYNAMIC` and the actual
- dimension needs to be provided as a constructor argument:
- .. code-block:: c++
- SphereManifold<ceres::DYNAMIC> manifold(ambient_dim);
- For more details, please see Section B.2 (p.25) in [Hertzberg]_
- :class:`LineManifold`
- ---------------------
- .. class:: LineManifold
- This class provides a manifold for lines, where the line is defined
- using an origin point and a direction vector. So the ambient size
- needs to be two times the dimension of the space in which the line
- lives. The first half of the parameter block is interpreted as the
- origin point and the second half as the direction. This manifold is a
- special case of the `Affine Grassmannian manifold
- <https://en.wikipedia.org/wiki/Affine_Grassmannian_(manifold))>`_ for
- the case :math:`\operatorname{Graff}_1(R^n)`.
- Note that this is a manifold for a line, rather than a point
- constrained to lie on a line. It is useful when one wants to optimize
- over the space of lines. For example, given :math:`n` distinct points
- in 3D (measurements) we want to find the line that minimizes the sum
- of squared distances to all the points.
- :class:`AutoDiffManifold`
- =========================
- .. class:: AutoDiffManifold
- Create a :class:`Manifold` with Jacobians computed via automatic
- differentiation.
- To get an auto differentiated manifold, you must define a Functor with
- templated ``Plus`` and ``Minus`` functions that compute:
- .. code-block:: c++
- x_plus_delta = Plus(x, delta);
- y_minus_x = Minus(y, x);
- Where, ``x``, ``y`` and ``x_plus_y`` are vectors on the manifold in
- the ambient space (so they are ``kAmbientSize`` vectors) and
- ``delta``, ``y_minus_x`` are vectors in the tangent space (so they are
- ``kTangentSize`` vectors).
- The Functor should have the signature:
- .. code-block:: c++
- struct Functor {
- template <typename T>
- bool Plus(const T* x, const T* delta, T* x_plus_delta) const;
- template <typename T>
- bool Minus(const T* y, const T* x, T* y_minus_x) const;
- };
- Observe that the ``Plus`` and ``Minus`` operations are templated on
- the parameter ``T``. The autodiff framework substitutes appropriate
- ``Jet`` objects for ``T`` in order to compute the derivative when
- necessary. This is the same mechanism that is used to compute
- derivatives when using :class:`AutoDiffCostFunction`.
- ``Plus`` and ``Minus`` should return true if the computation is
- successful and false otherwise, in which case the result will not be
- used.
- Given this Functor, the corresponding :class:`Manifold` can be constructed as:
- .. code-block:: c++
- AutoDiffManifold<Functor, kAmbientSize, kTangentSize> manifold;
- .. NOTE::
- The following is only used for illustration purposes. Ceres Solver
- ships with an optimized, production grade :class:`QuaternionManifold`
- implementation.
- As a concrete example consider the case of `Quaternions
- <https://en.wikipedia.org/wiki/Quaternion>`_. Quaternions form a three
- dimensional manifold embedded in :math:`\mathbb{R}^4`, i.e. they have
- an ambient dimension of 4 and their tangent space has dimension 3. The
- following Functor defines the ``Plus`` and ``Minus`` operations on the
- Quaternion manifold. It assumes that the quaternions are laid out as
- ``[w,x,y,z]`` in memory, i.e. the real or scalar part is the first
- coordinate.
- .. code-block:: c++
- struct QuaternionFunctor {
- template <typename T>
- bool Plus(const T* x, const T* delta, T* x_plus_delta) const {
- const T squared_norm_delta =
- delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2];
- T q_delta[4];
- if (squared_norm_delta > T(0.0)) {
- T norm_delta = sqrt(squared_norm_delta);
- const T sin_delta_by_delta = sin(norm_delta) / norm_delta;
- q_delta[0] = cos(norm_delta);
- q_delta[1] = sin_delta_by_delta * delta[0];
- q_delta[2] = sin_delta_by_delta * delta[1];
- q_delta[3] = sin_delta_by_delta * delta[2];
- } else {
- // We do not just use q_delta = [1,0,0,0] here because that is a
- // constant and when used for automatic differentiation will
- // lead to a zero derivative. Instead we take a first order
- // approximation and evaluate it at zero.
- q_delta[0] = T(1.0);
- q_delta[1] = delta[0];
- q_delta[2] = delta[1];
- q_delta[3] = delta[2];
- }
- QuaternionProduct(q_delta, x, x_plus_delta);
- return true;
- }
- template <typename T>
- bool Minus(const T* y, const T* x, T* y_minus_x) const {
- T minus_x[4] = {x[0], -x[1], -x[2], -x[3]};
- T ambient_y_minus_x[4];
- QuaternionProduct(y, minus_x, ambient_y_minus_x);
- T u_norm = sqrt(ambient_y_minus_x[1] * ambient_y_minus_x[1] +
- ambient_y_minus_x[2] * ambient_y_minus_x[2] +
- ambient_y_minus_x[3] * ambient_y_minus_x[3]);
- if (u_norm > 0.0) {
- T theta = atan2(u_norm, ambient_y_minus_x[0]);
- y_minus_x[0] = theta * ambient_y_minus_x[1] / u_norm;
- y_minus_x[1] = theta * ambient_y_minus_x[2] / u_norm;
- y_minus_x[2] = theta * ambient_y_minus_x[3] / u_norm;
- } else {
- We do not use [0,0,0] here because even though the value part is
- a constant, the derivative part is not.
- y_minus_x[0] = ambient_y_minus_x[1];
- y_minus_x[1] = ambient_y_minus_x[2];
- y_minus_x[2] = ambient_y_minus_x[3];
- }
- return true;
- }
- };
- Then given this struct, the auto differentiated Quaternion Manifold can now
- be constructed as
- .. code-block:: c++
- Manifold* manifold = new AutoDiffManifold<QuaternionFunctor, 4, 3>;
- :class:`Problem`
- ================
- .. class:: Problem
- :class:`Problem` holds the robustified bounds constrained
- non-linear least squares problem :eq:`ceresproblem_modeling`. To
- create a least squares problem, use the
- :func:`Problem::AddResidalBlock` and
- :func:`Problem::AddParameterBlock` methods.
- For example a problem containing 3 parameter blocks of sizes 3, 4
- and 5 respectively and two residual blocks of size 2 and 6:
- .. code-block:: c++
- double x1[] = { 1.0, 2.0, 3.0 };
- double x2[] = { 1.0, 2.0, 3.0, 5.0 };
- double x3[] = { 1.0, 2.0, 3.0, 6.0, 7.0 };
- Problem problem;
- problem.AddResidualBlock(new MyUnaryCostFunction(...), x1);
- problem.AddResidualBlock(new MyBinaryCostFunction(...), x2, x3);
- :func:`Problem::AddResidualBlock` as the name implies, adds a
- residual block to the problem. It adds a :class:`CostFunction`, an
- optional :class:`LossFunction` and connects the
- :class:`CostFunction` to a set of parameter block.
- The cost function carries with it information about the sizes of
- the parameter blocks it expects. The function checks that these
- match the sizes of the parameter blocks listed in
- ``parameter_blocks``. The program aborts if a mismatch is
- detected. ``loss_function`` can be ``nullptr``, in which case the cost
- of the term is just the squared norm of the residuals.
- The user has the option of explicitly adding the parameter blocks
- using :func:`Problem::AddParameterBlock`. This causes additional
- correctness checking; however, :func:`Problem::AddResidualBlock`
- implicitly adds the parameter blocks if they are not present, so
- calling :func:`Problem::AddParameterBlock` explicitly is not
- required.
- :func:`Problem::AddParameterBlock` explicitly adds a parameter
- block to the :class:`Problem`. Optionally it allows the user to
- associate a :class:`Manifold` object with the parameter block
- too. Repeated calls with the same arguments are ignored. Repeated
- calls with the same double pointer but a different size results in
- undefined behavior.
- You can set any parameter block to be constant using
- :func:`Problem::SetParameterBlockConstant` and undo this using
- :func:`SetParameterBlockVariable`.
- In fact you can set any number of parameter blocks to be constant,
- and Ceres is smart enough to figure out what part of the problem
- you have constructed depends on the parameter blocks that are free
- to change and only spends time solving it. So for example if you
- constructed a problem with a million parameter blocks and 2 million
- residual blocks, but then set all but one parameter blocks to be
- constant and say only 10 residual blocks depend on this one
- non-constant parameter block. Then the computational effort Ceres
- spends in solving this problem will be the same if you had defined
- a problem with one parameter block and 10 residual blocks.
- **Ownership**
- :class:`Problem` by default takes ownership of the
- ``cost_function``, ``loss_function`` and ``manifold`` pointers. These
- objects remain live for the life of the :class:`Problem`. If the user wishes
- to keep control over the destruction of these objects, then they can do this
- by setting the corresponding enums in the :class:`Problem::Options` struct.
- Note that even though the Problem takes ownership of objects,
- ``cost_function`` and ``loss_function``, it does not preclude the
- user from re-using them in another residual block. Similarly the
- same ``manifold`` object can be used with multiple parameter blocks. The
- destructor takes care to call delete on each owned object exactly once.
- .. class:: Problem::Options
- Options struct that is used to control :class:`Problem`.
- .. member:: Ownership Problem::Options::cost_function_ownership
- Default: ``TAKE_OWNERSHIP``
- This option controls whether the Problem object owns the cost
- functions.
- If set to ``TAKE_OWNERSHIP``, then the problem object will delete the
- cost functions on destruction. The destructor is careful to delete
- the pointers only once, since sharing cost functions is allowed.
- .. member:: Ownership Problem::Options::loss_function_ownership
- Default: ``TAKE_OWNERSHIP``
- This option controls whether the Problem object owns the loss
- functions.
- If set to ``TAKE_OWNERSHIP``, then the problem object will delete the
- loss functions on destruction. The destructor is careful to delete
- the pointers only once, since sharing loss functions is allowed.
- .. member:: Ownership Problem::Options::manifold_ownership
- Default: ``TAKE_OWNERSHIP``
- This option controls whether the Problem object owns the manifolds.
- If set to ``TAKE_OWNERSHIP``, then the problem object will delete the
- manifolds on destruction. The destructor is careful to delete the
- pointers only once, since sharing manifolds is allowed.
- .. member:: bool Problem::Options::enable_fast_removal
- Default: ``false``
- If true, trades memory for faster
- :func:`Problem::RemoveResidualBlock` and
- :func:`Problem::RemoveParameterBlock` operations.
- By default, :func:`Problem::RemoveParameterBlock` and
- :func:`Problem::RemoveResidualBlock` take time proportional to
- the size of the entire problem. If you only ever remove
- parameters or residuals from the problem occasionally, this might
- be acceptable. However, if you have memory to spare, enable this
- option to make :func:`Problem::RemoveParameterBlock` take time
- proportional to the number of residual blocks that depend on it,
- and :func:`Problem::RemoveResidualBlock` take (on average)
- constant time.
- The increase in memory usage is twofold: an additional hash set
- per parameter block containing all the residuals that depend on
- the parameter block; and a hash set in the problem containing all
- residuals.
- .. member:: bool Problem::Options::disable_all_safety_checks
- Default: `false`
- By default, Ceres performs a variety of safety checks when
- constructing the problem. There is a small but measurable
- performance penalty to these checks, typically around 5% of
- construction time. If you are sure your problem construction is
- correct, and 5% of the problem construction time is truly an
- overhead you want to avoid, then you can set
- disable_all_safety_checks to true.
- .. warning::
- Do not set this to true, unless you are absolutely sure of what you are
- doing.
- .. member:: Context* Problem::Options::context
- Default: ``nullptr``
- A Ceres global context to use for solving this problem. This may
- help to reduce computation time as Ceres can reuse expensive
- objects to create. The context object can be `nullptr`, in which
- case Ceres may create one.
- Ceres does NOT take ownership of the pointer.
- .. member:: EvaluationCallback* Problem::Options::evaluation_callback
- Default: ``nullptr``
- Using this callback interface, Ceres will notify you when it is
- about to evaluate the residuals or Jacobians.
- If an ``evaluation_callback`` is present, Ceres will update the
- user's parameter blocks to the values that will be used when
- calling :func:`CostFunction::Evaluate` before calling
- :func:`EvaluationCallback::PrepareForEvaluation`. One can then use
- this callback to share (or cache) computation between cost
- functions by doing the shared computation in
- :func:`EvaluationCallback::PrepareForEvaluation` before Ceres
- calls :func:`CostFunction::Evaluate`.
- Problem does NOT take ownership of the callback.
- .. NOTE::
- Evaluation callbacks are incompatible with inner iterations. So
- calling Solve with
- :member:`Solver::Options::use_inner_iterations` set to ``true``
- on a :class:`Problem` with a non-null evaluation callback is an
- error.
- .. function:: ResidualBlockId Problem::AddResidualBlock(CostFunction* cost_function, LossFunction* loss_function, const std::vector<double*> parameter_blocks)
- .. function:: template <typename Ts...> ResidualBlockId Problem::AddResidualBlock(CostFunction* cost_function, LossFunction* loss_function, double* x0, Ts... xs)
- Add a residual block to the overall cost function. The cost
- function carries with it information about the sizes of the
- parameter blocks it expects. The function checks that these match
- the sizes of the parameter blocks listed in parameter_blocks. The
- program aborts if a mismatch is detected. loss_function can be
- ``nullptr``, in which case the cost of the term is just the squared
- norm of the residuals.
- The parameter blocks may be passed together as a
- ``vector<double*>``, or ``double*`` pointers.
- The user has the option of explicitly adding the parameter blocks
- using AddParameterBlock. This causes additional correctness
- checking; however, AddResidualBlock implicitly adds the parameter
- blocks if they are not present, so calling AddParameterBlock
- explicitly is not required.
- The Problem object by default takes ownership of the
- cost_function and loss_function pointers. These objects remain
- live for the life of the Problem object. If the user wishes to
- keep control over the destruction of these objects, then they can
- do this by setting the corresponding enums in the Options struct.
- .. note::
- Even though the Problem takes ownership of ``cost_function``
- and ``loss_function``, it does not preclude the user from re-using
- them in another residual block. The destructor takes care to call
- delete on each cost_function or loss_function pointer only once,
- regardless of how many residual blocks refer to them.
- Example usage:
- .. code-block:: c++
- double x1[] = {1.0, 2.0, 3.0};
- double x2[] = {1.0, 2.0, 5.0, 6.0};
- double x3[] = {3.0, 6.0, 2.0, 5.0, 1.0};
- std::vector<double*> v1;
- v1.push_back(x1);
- std::vector<double*> v2;
- v2.push_back(x2);
- v2.push_back(x1);
- Problem problem;
- problem.AddResidualBlock(new MyUnaryCostFunction(...), nullptr, x1);
- problem.AddResidualBlock(new MyBinaryCostFunction(...), nullptr, x2, x1);
- problem.AddResidualBlock(new MyUnaryCostFunction(...), nullptr, v1);
- problem.AddResidualBlock(new MyBinaryCostFunction(...), nullptr, v2);
- .. function:: void Problem::AddParameterBlock(double* values, int size, Manifold* manifold)
- Add a parameter block with appropriate size and Manifold to the
- problem. It is okay for ``manifold`` to be ``nullptr``.
- Repeated calls with the same arguments are ignored. Repeated calls
- with the same double pointer but a different size results in a crash
- (unless :member:`Solver::Options::disable_all_safety_checks` is set to true).
- Repeated calls with the same double pointer and size but different
- :class:`Manifold` is equivalent to calling `SetManifold(manifold)`,
- i.e., any previously associated :class:`Manifold` object will be replaced
- with the `manifold`.
- .. function:: void Problem::AddParameterBlock(double* values, int size)
- Add a parameter block with appropriate size and parameterization to
- the problem. Repeated calls with the same arguments are
- ignored. Repeated calls with the same double pointer but a
- different size results in undefined behavior.
- .. function:: void Problem::RemoveResidualBlock(ResidualBlockId residual_block)
- Remove a residual block from the problem.
- Since residual blocks are allowed to share cost function and loss
- function objects, Ceres Solver uses a reference counting
- mechanism. So when a residual block is deleted, the reference count
- for the corresponding cost function and loss function objects are
- decreased and when this count reaches zero, they are deleted.
- If :member:`Problem::Options::enable_fast_removal` is ``true``, then the removal
- is fast (almost constant time). Otherwise it is linear, requiring a
- scan of the entire problem.
- Removing a residual block has no effect on the parameter blocks
- that the problem depends on.
- .. warning::
- Removing a residual or parameter block will destroy the implicit
- ordering, rendering the jacobian or residuals returned from the solver
- uninterpretable. If you depend on the evaluated jacobian, do not use
- remove! This may change in a future release. Hold the indicated parameter
- block constant during optimization.
- .. function:: void Problem::RemoveParameterBlock(const double* values)
- Remove a parameter block from the problem. Any residual blocks that
- depend on the parameter are also removed, as described above in
- :func:`RemoveResidualBlock()`.
- The manifold of the parameter block, if it exists, will persist until the
- deletion of the problem.
- If :member:`Problem::Options::enable_fast_removal` is ``true``, then the removal
- is fast (almost constant time). Otherwise, removing a parameter
- block will scan the entire Problem.
- .. warning::
- Removing a residual or parameter block will destroy the implicit
- ordering, rendering the jacobian or residuals returned from the solver
- uninterpretable. If you depend on the evaluated jacobian, do not use
- remove! This may change in a future release.
- .. function:: void Problem::SetParameterBlockConstant(const double* values)
- Hold the indicated parameter block constant during optimization.
- .. function:: void Problem::SetParameterBlockVariable(double* values)
- Allow the indicated parameter to vary during optimization.
- .. function:: bool Problem::IsParameterBlockConstant(const double* values) const
- Returns ``true`` if a parameter block is set constant, and false
- otherwise. A parameter block may be set constant in two ways:
- either by calling ``SetParameterBlockConstant`` or by associating a
- :class:`Manifold` with a zero dimensional tangent space with it.
- .. function:: void SetManifold(double* values, Manifold* manifold);
- Set the :class:`Manifold` for the parameter block. Calling
- :func:`Problem::SetManifold` with ``nullptr`` will clear any
- previously set :class:`Manifold` for the parameter block.
- Repeated calls will result in any previously associated
- :class:`Manifold` object to be replaced with ``manifold``.
- ``manifold`` is owned by :class:`Problem` by default (See
- :class:`Problem::Options` to override this behaviour).
- It is acceptable to set the same :class:`Manifold` for multiple
- parameter blocks.
- .. function:: const Manifold* GetManifold(const double* values) const;
- Get the :class:`Manifold` object associated with this parameter block.
- If there is no :class:`Manifold` object associated with the parameter block,
- then ``nullptr`` is returned.
- .. function:: bool HasManifold(const double* values) const;
- Returns ``true`` if a :class:`Manifold` is associated with this parameter
- block, ``false`` otherwise.
- .. function:: void Problem::SetParameterLowerBound(double* values, int index, double lower_bound)
- Set the lower bound for the parameter at position `index` in the
- parameter block corresponding to `values`. By default the lower
- bound is ``-std::numeric_limits<double>::max()``, which is treated
- by the solver as the same as :math:`-\infty`.
- .. function:: void Problem::SetParameterUpperBound(double* values, int index, double upper_bound)
- Set the upper bound for the parameter at position `index` in the
- parameter block corresponding to `values`. By default the value is
- ``std::numeric_limits<double>::max()``, which is treated by the
- solver as the same as :math:`\infty`.
- .. function:: double Problem::GetParameterLowerBound(const double* values, int index)
- Get the lower bound for the parameter with position `index`. If the
- parameter is not bounded by the user, then its lower bound is
- ``-std::numeric_limits<double>::max()``.
- .. function:: double Problem::GetParameterUpperBound(const double* values, int index)
- Get the upper bound for the parameter with position `index`. If the
- parameter is not bounded by the user, then its upper bound is
- ``std::numeric_limits<double>::max()``.
- .. function:: int Problem::NumParameterBlocks() const
- Number of parameter blocks in the problem. Always equals
- parameter_blocks().size() and parameter_block_sizes().size().
- .. function:: int Problem::NumParameters() const
- The size of the parameter vector obtained by summing over the sizes
- of all the parameter blocks.
- .. function:: int Problem::NumResidualBlocks() const
- Number of residual blocks in the problem. Always equals
- residual_blocks().size().
- .. function:: int Problem::NumResiduals() const
- The size of the residual vector obtained by summing over the sizes
- of all of the residual blocks.
- .. function:: int Problem::ParameterBlockSize(const double* values) const
- The size of the parameter block.
- .. function:: int Problem::ParameterBlockTangentSize(const double* values) const
- The dimension of the tangent space of the :class:`Manifold` for the
- parameter block. If there is no :class:`Manifold` associated with this
- parameter block, then ``ParameterBlockTangentSize = ParameterBlockSize``.
- .. function:: bool Problem::HasParameterBlock(const double* values) const
- Is the given parameter block present in the problem or not?
- .. function:: void Problem::GetParameterBlocks(std::vector<double*>* parameter_blocks) const
- Fills the passed ``parameter_blocks`` vector with pointers to the
- parameter blocks currently in the problem. After this call,
- ``parameter_block.size() == NumParameterBlocks``.
- .. function:: void Problem::GetResidualBlocks(std::vector<ResidualBlockId>* residual_blocks) const
- Fills the passed `residual_blocks` vector with pointers to the
- residual blocks currently in the problem. After this call,
- `residual_blocks.size() == NumResidualBlocks`.
- .. function:: void Problem::GetParameterBlocksForResidualBlock(const ResidualBlockId residual_block, std::vector<double*>* parameter_blocks) const
- Get all the parameter blocks that depend on the given residual
- block.
- .. function:: void Problem::GetResidualBlocksForParameterBlock(const double* values, std::vector<ResidualBlockId>* residual_blocks) const
- Get all the residual blocks that depend on the given parameter
- block.
- If :member:`Problem::Options::enable_fast_removal` is
- ``true``, then getting the residual blocks is fast and depends only
- on the number of residual blocks. Otherwise, getting the residual
- blocks for a parameter block will scan the entire problem.
- .. function:: const CostFunction* Problem::GetCostFunctionForResidualBlock(const ResidualBlockId residual_block) const
- Get the :class:`CostFunction` for the given residual block.
- .. function:: const LossFunction* Problem::GetLossFunctionForResidualBlock(const ResidualBlockId residual_block) const
- Get the :class:`LossFunction` for the given residual block.
- .. function:: bool EvaluateResidualBlock(ResidualBlockId residual_block_id, bool apply_loss_function, double* cost,double* residuals, double** jacobians) const
- Evaluates the residual block, storing the scalar cost in ``cost``, the
- residual components in ``residuals``, and the jacobians between the
- parameters and residuals in ``jacobians[i]``, in row-major order.
- If ``residuals`` is ``nullptr``, the residuals are not computed.
- If ``jacobians`` is ``nullptr``, no Jacobians are computed. If
- ``jacobians[i]`` is ``nullptr``, then the Jacobian for that
- parameter block is not computed.
- It is not okay to request the Jacobian w.r.t a parameter block
- that is constant.
- The return value indicates the success or failure. Even if the
- function returns false, the caller should expect the output
- memory locations to have been modified.
- The returned cost and jacobians have had robustification and
- :class:`Manifold` applied already; for example, the jacobian for a
- 4-dimensional quaternion parameter using the :class:`QuaternionManifold` is
- ``num_residuals x 3`` instead of ``num_residuals x 4``.
- ``apply_loss_function`` as the name implies allows the user to
- switch the application of the loss function on and off.
- .. NOTE:: If an :class:`EvaluationCallback` is associated with the
- problem, then its
- :func:`EvaluationCallback::PrepareForEvaluation` method will be
- called every time this method is called with `new_point =
- true`. This conservatively assumes that the user may have
- changed the parameter values since the previous call to evaluate
- / solve. For improved efficiency, and only if you know that the
- parameter values have not changed between calls, see
- :func:`Problem::EvaluateResidualBlockAssumingParametersUnchanged`.
- .. function:: bool EvaluateResidualBlockAssumingParametersUnchanged(ResidualBlockId residual_block_id, bool apply_loss_function, double* cost,double* residuals, double** jacobians) const
- Same as :func:`Problem::EvaluateResidualBlock` except that if an
- :class:`EvaluationCallback` is associated with the problem, then
- its :func:`EvaluationCallback::PrepareForEvaluation` method will
- be called every time this method is called with new_point = false.
- This means, if an :class:`EvaluationCallback` is associated with
- the problem then it is the user's responsibility to call
- :func:`EvaluationCallback::PrepareForEvaluation` before calling
- this method if necessary, i.e. iff the parameter values have been
- changed since the last call to evaluate / solve.'
- This is because, as the name implies, we assume that the parameter
- blocks did not change since the last time
- :func:`EvaluationCallback::PrepareForEvaluation` was called (via
- :func:`Solve`, :func:`Problem::Evaluate` or
- :func:`Problem::EvaluateResidualBlock`).
- .. function:: bool Problem::Evaluate(const Problem::EvaluateOptions& options, double* cost, std::vector<double>* residuals, std::vector<double>* gradient, CRSMatrix* jacobian)
- Evaluate a :class:`Problem`. Any of the output pointers can be
- ``nullptr``. Which residual blocks and parameter blocks are used is
- controlled by the :class:`Problem::EvaluateOptions` struct below.
- .. NOTE::
- The evaluation will use the values stored in the memory
- locations pointed to by the parameter block pointers used at the
- time of the construction of the problem, for example in the
- following code:
- .. code-block:: c++
- Problem problem;
- double x = 1;
- problem.Add(new MyCostFunction, nullptr, &x);
- double cost = 0.0;
- problem.Evaluate(Problem::EvaluateOptions(), &cost, nullptr, nullptr, nullptr);
- The cost is evaluated at `x = 1`. If you wish to evaluate the
- problem at `x = 2`, then
- .. code-block:: c++
- x = 2;
- problem.Evaluate(Problem::EvaluateOptions(), &cost, nullptr, nullptr, nullptr);
- is the way to do so.
- .. NOTE::
- If no :class:`Manifold` are used, then the size of the gradient vector is
- the sum of the sizes of all the parameter blocks. If a parameter block has
- a manifold then it contributes "TangentSize" entries to the gradient
- vector.
- .. NOTE::
- This function cannot be called while the problem is being
- solved, for example it cannot be called from an
- :class:`IterationCallback` at the end of an iteration during a
- solve.
- .. NOTE::
- If an EvaluationCallback is associated with the problem, then
- its PrepareForEvaluation method will be called everytime this
- method is called with ``new_point = true``.
- .. class:: Problem::EvaluateOptions
- Options struct that is used to control :func:`Problem::Evaluate`.
- .. member:: std::vector<double*> Problem::EvaluateOptions::parameter_blocks
- The set of parameter blocks for which evaluation should be
- performed. This vector determines the order in which parameter
- blocks occur in the gradient vector and in the columns of the
- jacobian matrix. If parameter_blocks is empty, then it is assumed
- to be equal to a vector containing ALL the parameter
- blocks. Generally speaking the ordering of the parameter blocks in
- this case depends on the order in which they were added to the
- problem and whether or not the user removed any parameter blocks.
- **NOTE** This vector should contain the same pointers as the ones
- used to add parameter blocks to the Problem. These parameter block
- should NOT point to new memory locations. Bad things will happen if
- you do.
- .. member:: std::vector<ResidualBlockId> Problem::EvaluateOptions::residual_blocks
- The set of residual blocks for which evaluation should be
- performed. This vector determines the order in which the residuals
- occur, and how the rows of the jacobian are ordered. If
- residual_blocks is empty, then it is assumed to be equal to the
- vector containing all the residual blocks.
- .. member:: bool Problem::EvaluateOptions::apply_loss_function
- Even though the residual blocks in the problem may contain loss
- functions, setting apply_loss_function to false will turn off the
- application of the loss function to the output of the cost
- function. This is of use for example if the user wishes to analyse
- the solution quality by studying the distribution of residuals
- before and after the solve.
- .. member:: int Problem::EvaluateOptions::num_threads
- Number of threads to use.
- :class:`EvaluationCallback`
- ===========================
- .. class:: EvaluationCallback
- Interface for receiving callbacks before Ceres evaluates residuals or
- Jacobians:
- .. code-block:: c++
- class EvaluationCallback {
- public:
- virtual ~EvaluationCallback();
- virtual void PrepareForEvaluation(bool evaluate_jacobians,
- bool new_evaluation_point) = 0;
- };
- .. function:: void EvaluationCallback::PrepareForEvaluation(bool evaluate_jacobians, bool new_evaluation_point)
- Ceres will call :func:`EvaluationCallback::PrepareForEvaluation`
- every time, and once before it computes the residuals and/or the
- Jacobians.
- User parameters (the double* values provided by the user) are fixed
- until the next call to
- :func:`EvaluationCallback::PrepareForEvaluation`. If
- ``new_evaluation_point == true``, then this is a new point that is
- different from the last evaluated point. Otherwise, it is the same
- point that was evaluated previously (either Jacobian or residual)
- and the user can use cached results from previous evaluations. If
- ``evaluate_jacobians`` is ``true``, then Ceres will request Jacobians
- in the upcoming cost evaluation.
- Using this callback interface, Ceres can notify you when it is
- about to evaluate the residuals or Jacobians. With the callback,
- you can share computation between residual blocks by doing the
- shared computation in
- :func:`EvaluationCallback::PrepareForEvaluation` before Ceres calls
- :func:`CostFunction::Evaluate` on all the residuals. It also
- enables caching results between a pure residual evaluation and a
- residual & Jacobian evaluation, via the ``new_evaluation_point``
- argument.
- One use case for this callback is if the cost function compute is
- moved to the GPU. In that case, the prepare call does the actual
- cost function evaluation, and subsequent calls from Ceres to the
- actual cost functions merely copy the results from the GPU onto the
- corresponding blocks for Ceres to plug into the solver.
- **Note**: Ceres provides no mechanism to share data other than the
- notification from the callback. Users must provide access to
- pre-computed shared data to their cost functions behind the scenes;
- this all happens without Ceres knowing. One approach is to put a
- pointer to the shared data in each cost function (recommended) or
- to use a global shared variable (discouraged; bug-prone). As far
- as Ceres is concerned, it is evaluating cost functions like any
- other; it just so happens that behind the scenes the cost functions
- reuse pre-computed data to execute faster. See
- `examples/evaluation_callback_example.cc
- <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/evaluation_callback_example.cc>`_
- for an example.
- See ``evaluation_callback_test.cc`` for code that explicitly
- verifies the preconditions between
- :func:`EvaluationCallback::PrepareForEvaluation` and
- :func:`CostFunction::Evaluate`.
- ``rotation.h``
- ==============
- Many applications of Ceres Solver involve optimization problems where
- some of the variables correspond to rotations. To ease the pain of
- work with the various representations of rotations (angle-axis,
- quaternion and matrix) we provide a handy set of templated
- functions. These functions are templated so that the user can use them
- within Ceres Solver's automatic differentiation framework.
- .. function:: template <typename T> void AngleAxisToQuaternion(T const* angle_axis, T* quaternion)
- Convert a value in combined axis-angle representation to a
- quaternion.
- The value ``angle_axis`` is a triple whose norm is an angle in radians,
- and whose direction is aligned with the axis of rotation, and
- ``quaternion`` is a 4-tuple that will contain the resulting quaternion.
- .. function:: template <typename T> void QuaternionToAngleAxis(T const* quaternion, T* angle_axis)
- Convert a quaternion to the equivalent combined axis-angle
- representation.
- The value ``quaternion`` must be a unit quaternion - it is not
- normalized first, and ``angle_axis`` will be filled with a value
- whose norm is the angle of rotation in radians, and whose direction
- is the axis of rotation.
- .. function:: template <typename T, int row_stride, int col_stride> void RotationMatrixToAngleAxis(const MatrixAdapter<const T, row_stride, col_stride>& R, T * angle_axis)
- .. function:: template <typename T, int row_stride, int col_stride> void AngleAxisToRotationMatrix(T const * angle_axis, const MatrixAdapter<T, row_stride, col_stride>& R)
- .. function:: template <typename T> void RotationMatrixToAngleAxis(T const * R, T * angle_axis)
- .. function:: template <typename T> void AngleAxisToRotationMatrix(T const * angle_axis, T * R)
- Conversions between :math:`3\times3` rotation matrix with given column and row strides and
- axis-angle rotation representations. The functions that take a pointer to T instead
- of a MatrixAdapter assume a column major representation with unit row stride and a column stride of 3.
- .. function:: template <typename T, int row_stride, int col_stride> void EulerAnglesToRotationMatrix(const T* euler, const MatrixAdapter<T, row_stride, col_stride>& R)
- .. function:: template <typename T> void EulerAnglesToRotationMatrix(const T* euler, int row_stride, T* R)
- Conversions between :math:`3\times3` rotation matrix with given column and row strides and
- Euler angle (in degrees) rotation representations.
- The {pitch,roll,yaw} Euler angles are rotations around the {x,y,z}
- axes, respectively. They are applied in that same order, so the
- total rotation R is Rz * Ry * Rx.
- The function that takes a pointer to T as the rotation matrix assumes a row
- major representation with unit column stride and a row stride of 3.
- The additional parameter row_stride is required to be 3.
- .. function:: template <typename T, int row_stride, int col_stride> void QuaternionToScaledRotation(const T q[4], const MatrixAdapter<T, row_stride, col_stride>& R)
- .. function:: template <typename T> void QuaternionToScaledRotation(const T q[4], T R[3 * 3])
- Convert a 4-vector to a :math:`3\times3` scaled rotation matrix.
- The choice of rotation is such that the quaternion
- :math:`\begin{bmatrix} 1 &0 &0 &0\end{bmatrix}` goes to an identity
- matrix and for small :math:`a, b, c` the quaternion
- :math:`\begin{bmatrix}1 &a &b &c\end{bmatrix}` goes to the matrix
- .. math::
- I + 2 \begin{bmatrix} 0 & -c & b \\ c & 0 & -a\\ -b & a & 0
- \end{bmatrix} + O(q^2)
- which corresponds to a Rodrigues approximation, the last matrix
- being the cross-product matrix of :math:`\begin{bmatrix} a& b&
- c\end{bmatrix}`. Together with the property that :math:`R(q_1 \otimes q_2)
- = R(q_1) R(q_2)` this uniquely defines the mapping from :math:`q` to
- :math:`R`.
- In the function that accepts a pointer to T instead of a MatrixAdapter,
- the rotation matrix ``R`` is a row-major matrix with unit column stride
- and a row stride of 3.
- No normalization of the quaternion is performed, i.e.
- :math:`R = \|q\|^2 Q`, where :math:`Q` is an orthonormal matrix
- such that :math:`\det(Q) = 1` and :math:`QQ' = I`.
- .. function:: template <typename T> void QuaternionToRotation(const T q[4], const MatrixAdapter<T, row_stride, col_stride>& R)
- .. function:: template <typename T> void QuaternionToRotation(const T q[4], T R[3 * 3])
- Same as above except that the rotation matrix is normalized by the
- Frobenius norm, so that :math:`R R' = I` (and :math:`\det(R) = 1`).
- .. function:: template <typename T> void UnitQuaternionRotatePoint(const T q[4], const T pt[3], T result[3])
- Rotates a point pt by a quaternion q:
- .. math:: \text{result} = R(q) \text{pt}
- Assumes the quaternion is unit norm. If you pass in a quaternion
- with :math:`|q|^2 = 2` then you WILL NOT get back 2 times the
- result you get for a unit quaternion.
- .. function:: template <typename T> void QuaternionRotatePoint(const T q[4], const T pt[3], T result[3])
- With this function you do not need to assume that :math:`q` has unit norm.
- It does assume that the norm is non-zero.
- .. function:: template <typename T> void QuaternionProduct(const T z[4], const T w[4], T zw[4])
- .. math:: zw = z \otimes w
- where :math:`\otimes` is the Quaternion product between 4-vectors.
- .. function:: template <typename T> void CrossProduct(const T x[3], const T y[3], T x_cross_y[3])
- .. math:: \text{x_cross_y} = x \times y
- .. function:: template <typename T> void AngleAxisRotatePoint(const T angle_axis[3], const T pt[3], T result[3])
- .. math:: y = R(\text{angle_axis}) x
- Cubic Interpolation
- ===================
- Optimization problems often involve functions that are given in the
- form of a table of values, for example an image. Evaluating these
- functions and their derivatives requires interpolating these
- values. Interpolating tabulated functions is a vast area of research
- and there are a lot of libraries which implement a variety of
- interpolation schemes. However, using them within the automatic
- differentiation framework in Ceres is quite painful. To this end,
- Ceres provides the ability to interpolate one dimensional and two
- dimensional tabular functions.
- The one dimensional interpolation is based on the Cubic Hermite
- Spline, also known as the Catmull-Rom Spline. This produces a first
- order differentiable interpolating function. The two dimensional
- interpolation scheme is a generalization of the one dimensional scheme
- where the interpolating function is assumed to be separable in the two
- dimensions,
- More details of the construction can be found `Linear Methods for
- Image Interpolation <http://www.ipol.im/pub/art/2011/g_lmii/>`_ by
- Pascal Getreuer.
- .. class:: CubicInterpolator
- Given as input an infinite one dimensional grid, which provides the
- following interface.
- .. code::
- struct Grid1D {
- enum { DATA_DIMENSION = 2; };
- void GetValue(int n, double* f) const;
- };
- Where, ``GetValue`` gives us the value of a function :math:`f`
- (possibly vector valued) for any integer :math:`n` and the enum
- ``DATA_DIMENSION`` indicates the dimensionality of the function being
- interpolated. For example if you are interpolating rotations in
- axis-angle format over time, then ``DATA_DIMENSION = 3``.
- :class:`CubicInterpolator` uses Cubic Hermite splines to produce a
- smooth approximation to it that can be used to evaluate the
- :math:`f(x)` and :math:`f'(x)` at any point on the real number
- line. For example, the following code interpolates an array of four
- numbers.
- .. code::
- const double x[] = {1.0, 2.0, 5.0, 6.0};
- Grid1D<double, 1> array(x, 0, 4);
- CubicInterpolator interpolator(array);
- double f, dfdx;
- interpolator.Evaluate(1.5, &f, &dfdx);
- In the above code we use ``Grid1D`` a templated helper class that
- allows easy interfacing between ``C++`` arrays and
- :class:`CubicInterpolator`.
- ``Grid1D`` supports vector valued functions where the various
- coordinates of the function can be interleaved or stacked. It also
- allows the use of any numeric type as input, as long as it can be
- safely cast to a double.
- .. class:: BiCubicInterpolator
- Given as input an infinite two dimensional grid, which provides the
- following interface:
- .. code::
- struct Grid2D {
- enum { DATA_DIMENSION = 2 };
- void GetValue(int row, int col, double* f) const;
- };
- Where, ``GetValue`` gives us the value of a function :math:`f`
- (possibly vector valued) for any pair of integers :code:`row` and
- :code:`col` and the enum ``DATA_DIMENSION`` indicates the
- dimensionality of the function being interpolated. For example if you
- are interpolating a color image with three channels (Red, Green &
- Blue), then ``DATA_DIMENSION = 3``.
- :class:`BiCubicInterpolator` uses the cubic convolution interpolation
- algorithm of R. Keys [Keys]_, to produce a smooth approximation to it
- that can be used to evaluate the :math:`f(r,c)`, :math:`\frac{\partial
- f(r,c)}{\partial r}` and :math:`\frac{\partial f(r,c)}{\partial c}` at
- any any point in the real plane.
- For example the following code interpolates a two dimensional array.
- .. code::
- const double data[] = {1.0, 3.0, -1.0, 4.0,
- 3.6, 2.1, 4.2, 2.0,
- 2.0, 1.0, 3.1, 5.2};
- Grid2D<double, 1> array(data, 0, 3, 0, 4);
- BiCubicInterpolator interpolator(array);
- double f, dfdr, dfdc;
- interpolator.Evaluate(1.2, 2.5, &f, &dfdr, &dfdc);
- In the above code, the templated helper class ``Grid2D`` is used to
- make a ``C++`` array look like a two dimensional table to
- :class:`BiCubicInterpolator`.
- ``Grid2D`` supports row or column major layouts. It also supports
- vector valued functions where the individual coordinates of the
- function may be interleaved or stacked. It also allows the use of any
- numeric type as input, as long as it can be safely cast to double.
|