LCOV - code coverage report
Current view: top level - AP_Math - matrix_alg.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 199 232 85.8 %
Date: 2026-09-09 11:02:08 Functions: 11 24 45.8 %

          Line data    Source code
       1             : /*
       2             :  * matrix3.cpp
       3             :  * Copyright (C) Siddharth Bharat Purohit, 3DRobotics Inc. 2015
       4             :  *
       5             :  * This file is free software: you can redistribute it and/or modify it
       6             :  * under the terms of the GNU General Public License as published by the
       7             :  * Free Software Foundation, either version 3 of the License, or
       8             :  * (at your option) any later version.
       9             :  *
      10             :  * This file is distributed in the hope that it will be useful, but
      11             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      13             :  * See the GNU General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License along
      16             :  * with this program.  If not, see <http://www.gnu.org/licenses/>.
      17             :  */
      18             : 
      19             : #include <AP_HAL/AP_HAL.h>
      20             : #include "AP_Math.h"
      21             : 
      22             : #include <stdio.h>
      23             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
      24             : #include <fenv.h>
      25             : #endif
      26             : 
      27             : /*
      28             :  *    Does matrix multiplication of two regular/square matrices
      29             :  *
      30             :  *    @param     A,           Matrix A
      31             :  *    @param     B,           Matrix B
      32             :  *    @param     n,           dimemsion of square matrices
      33             :  *    @returns                multiplied matrix i.e. A*B
      34             :  */
      35             : template<typename T>
      36        2820 : static T* matrix_multiply(const T *A, const T *B, uint16_t n)
      37             : {
      38        2820 :     T* ret = NEW_NOTHROW T[n*n];
      39        2820 :     memset(ret,0.0f,n*n*sizeof(T));
      40             : 
      41       27300 :     for(uint16_t i = 0; i < n; i++) {
      42      239400 :         for(uint16_t j = 0; j < n; j++) {
      43     2116800 :             for(uint16_t k = 0;k < n; k++) {
      44     1901880 :                 ret[i*n + j] += A[i*n + k] * B[k*n + j];
      45             :             }
      46             :         }
      47             :     }
      48        2820 :     return ret;
      49             : }
      50             : 
      51             : template<typename T>
      52       11958 : static inline void swap(T &a, T &b)
      53             : {
      54             :     T c;
      55       11958 :     c = a;
      56       11958 :     a = b;
      57       11958 :     b = c;
      58       11958 : }
      59             : 
      60             : /*
      61             :  *    calculates pivot matrix such that all the larger elements in the row are on diagonal
      62             :  *
      63             :  *    @param     A,           input matrix matrix
      64             :  *    @param     pivot
      65             :  *    @param     n,           dimenstion of square matrix
      66             :  *    @returns                false = matrix is Singular or non positive definite, true = matrix inversion successful
      67             :  */
      68             : template<typename T>
      69         940 : static void mat_pivot(const T* A, T* pivot, uint16_t n)
      70             : {
      71        9100 :     for(uint16_t i = 0;i<n;i++){
      72       79800 :         for(uint16_t j=0;j<n;j++) {
      73       71640 :             pivot[i*n+j] = static_cast<T>(i==j);
      74             :         }
      75             :     }
      76             : 
      77        9100 :     for(uint16_t i = 0;i < n; i++) {
      78        8160 :         uint16_t max_j = i;
      79       48060 :         for(uint16_t j=i;j<n;j++){
      80       39900 :             if(fabsF(A[j*n + i]) > fabsF(A[max_j*n + i])) {
      81        3222 :                 max_j = j;
      82             :             }
      83             :         }
      84             : 
      85        8160 :         if(max_j != i) {
      86       13288 :             for(uint16_t k = 0; k < n; k++) {
      87       11958 :                 swap(pivot[i*n + k], pivot[max_j*n + k]);
      88             :             }
      89             :         }
      90             :     }
      91         940 : }
      92             : 
      93             : /*
      94             :  *    calculates matrix inverse of Lower trangular matrix using forward substitution
      95             :  *
      96             :  *    @param     L,           lower triangular matrix
      97             :  *    @param     out,         Output inverted lower triangular matrix
      98             :  *    @param     n,           dimension of matrix
      99             :  */
     100             : template<typename T>
     101         940 : static void mat_forward_sub(const T *L, T *out, uint16_t n)
     102             : {
     103             :     // Forward substitution solve LY = I
     104        9100 :     for(int i = 0; i < n; i++) {
     105        8160 :         out[i*n + i] = 1/L[i*n + i];
     106       39900 :         for (int j = i+1; j < n; j++) {
     107      136040 :             for (int k = i; k < j; k++) {
     108      104300 :                 out[j*n + i] -= L[j*n + k] * out[k*n + i];
     109             :             }
     110       31740 :             out[j*n + i] /= L[j*n + j];
     111             :         }
     112             :     }
     113         940 : }
     114             : 
     115             : /*
     116             :  *    calculates matrix inverse of Upper trangular matrix using backward substitution
     117             :  *
     118             :  *    @param     U,           upper triangular matrix
     119             :  *    @param     out,         Output inverted upper triangular matrix
     120             :  *    @param     n,           dimension of matrix
     121             :  */
     122             : template<typename T>
     123         940 : static void mat_back_sub(const T *U, T *out, uint16_t n)
     124             : {
     125             :     // Backward Substitution solve UY = I
     126        9100 :     for(int i = n-1; i >= 0; i--) {
     127        8160 :         out[i*n + i] = 1/U[i*n + i];
     128       39900 :         for (int j = i - 1; j >= 0; j--) {
     129      136040 :             for (int k = i; k > j; k--) {
     130      104300 :                 out[j*n + i] -= U[j*n + k] * out[k*n + i];
     131             :             }
     132       31740 :             out[j*n + i] /= U[j*n + j];
     133             :         }
     134             :     }
     135         940 : }
     136             : 
     137             : /*
     138             :  *    Decomposes square matrix into Lower and Upper triangular matrices such that
     139             :  *    A*P = L*U, where P is the pivot matrix
     140             :  *    ref: http://rosettacode.org/wiki/LU_decomposition
     141             :  *    @param     U,           upper triangular matrix
     142             :  *    @param     out,         Output inverted upper triangular matrix
     143             :  *    @param     n,           dimension of matrix
     144             :  */
     145             : template<typename T>
     146         940 : static void mat_LU_decompose(const T* A, T* L, T* U, T *P, uint16_t n)
     147             : {
     148         940 :     memset(L,0,n*n*sizeof(T));
     149         940 :     memset(U,0,n*n*sizeof(T));
     150         940 :     memset(P,0,n*n*sizeof(T));
     151         940 :     mat_pivot(A,P,n);
     152             : 
     153         940 :     T *APrime = matrix_multiply(P,A,n);
     154        9100 :     for(uint16_t i = 0; i < n; i++) {
     155        8160 :         L[i*n + i] = 1;
     156             :     }
     157        9100 :     for(uint16_t i = 0; i < n; i++) {
     158       79800 :         for(uint16_t j = 0; j < n; j++) {
     159       71640 :             if(j <= i) {    
     160       39900 :                 U[j*n + i] = APrime[j*n + i];
     161      144200 :                 for(uint16_t k = 0; k < j; k++) {
     162      104300 :                     U[j*n + i] -= L[j*n + k] * U[k*n + i]; 
     163             :                 }
     164             :             }
     165       71640 :             if(j >= i) {
     166       39900 :                 L[j*n + i] = APrime[j*n + i];
     167      144200 :                 for(uint16_t k = 0; k < i; k++) {
     168      104300 :                     L[j*n + i] -= L[j*n + k] * U[k*n + i]; 
     169             :                 }
     170       39900 :                 L[j*n + i] /= U[i*n + i];
     171             :             }
     172             :         }
     173             :     }
     174         940 :     delete[] APrime;
     175         940 : }
     176             : 
     177             : /*
     178             :  *    matrix inverse code for any square matrix using LU decomposition
     179             :  *    inv = inv(U)*inv(L)*P, where L and U are triagular matrices and P the pivot matrix
     180             :  *    ref: http://www.cl.cam.ac.uk/teaching/1314/NumMethods/supporting/mcmaster-kiruba-ludecomp.pdf
     181             :  *    @param     m,           input 4x4 matrix
     182             :  *    @param     inv,      Output inverted 4x4 matrix
     183             :  *    @param     n,           dimension of square matrix
     184             :  *    @returns                false = matrix is Singular, true = matrix inversion successful
     185             :  */
     186             : template<typename T>
     187         940 : static bool mat_inverseN(const T* A, T* inv, uint16_t n)
     188             : {
     189             :     T *L, *U, *P;
     190         940 :     bool ret = true;
     191         940 :     L = NEW_NOTHROW T[n*n];
     192         940 :     U = NEW_NOTHROW T[n*n];
     193         940 :     P = NEW_NOTHROW T[n*n];
     194         940 :     mat_LU_decompose(A,L,U,P,n);
     195             : 
     196         940 :     T *L_inv = NEW_NOTHROW T[n*n];
     197         940 :     T *U_inv = NEW_NOTHROW T[n*n];
     198             : 
     199         940 :     memset(L_inv,0,n*n*sizeof(T));
     200         940 :     mat_forward_sub(L,L_inv,n);
     201             : 
     202         940 :     memset(U_inv,0,n*n*sizeof(T));
     203         940 :     mat_back_sub(U,U_inv,n);
     204             : 
     205             :     // decomposed matrices no longer required
     206         940 :     delete[] L;
     207         940 :     delete[] U;
     208             : 
     209         940 :     T *inv_unpivoted = matrix_multiply(U_inv,L_inv,n);
     210         940 :     T *inv_pivoted = matrix_multiply(inv_unpivoted, P, n);
     211             : 
     212             :     //check sanity of results
     213        9100 :     for(uint16_t i = 0; i < n; i++) {
     214       79800 :         for(uint16_t j = 0; j < n; j++) {
     215       71640 :             if(isnan(inv_pivoted[i*n+j]) || isinf(inv_pivoted[i*n+j])){
     216           0 :                 ret = false;
     217             :             }
     218             :         }
     219             :     }
     220         940 :     memcpy(inv,inv_pivoted,n*n*sizeof(T));
     221             : 
     222             :     //free memory
     223         940 :     delete[] inv_pivoted;
     224         940 :     delete[] inv_unpivoted;
     225         940 :     delete[] P;
     226         940 :     delete[] U_inv;
     227         940 :     delete[] L_inv;
     228         940 :     return ret;
     229             : }
     230             : 
     231             : /*
     232             :  *    fast matrix inverse code only for 3x3 square matrix
     233             :  *
     234             :  *    @param     m,           input 4x4 matrix
     235             :  *    @param     invOut,      Output inverted 4x4 matrix
     236             :  *    @returns                false = matrix is Singular, true = matrix inversion successful
     237             :  */
     238             : template<typename T>
     239           0 : static bool inverse3x3(const T m[], T invOut[])
     240             : {
     241             :     T inv[9];
     242             :     // computes the inverse of a matrix m
     243           0 :     T  det = m[0] * (m[4] * m[8] - m[7] * m[5]) -
     244           0 :     m[1] * (m[3] * m[8] - m[5] * m[6]) +
     245           0 :     m[2] * (m[3] * m[7] - m[4] * m[6]);
     246           0 :     if (is_zero(det) || isinf(det)) {
     247           0 :         return false;
     248             :     }
     249             : 
     250           0 :     T invdet = 1 / det;
     251             : 
     252           0 :     inv[0] = (m[4] * m[8] - m[7] * m[5]) * invdet;
     253           0 :     inv[1] = (m[2] * m[7] - m[1] * m[8]) * invdet;
     254           0 :     inv[2] = (m[1] * m[5] - m[2] * m[4]) * invdet;
     255           0 :     inv[3] = (m[5] * m[6] - m[3] * m[8]) * invdet;
     256           0 :     inv[4] = (m[0] * m[8] - m[2] * m[6]) * invdet;
     257           0 :     inv[5] = (m[3] * m[2] - m[0] * m[5]) * invdet;
     258           0 :     inv[6] = (m[3] * m[7] - m[6] * m[4]) * invdet;
     259           0 :     inv[7] = (m[6] * m[1] - m[0] * m[7]) * invdet;
     260           0 :     inv[8] = (m[0] * m[4] - m[3] * m[1]) * invdet;
     261             : 
     262           0 :     for(uint16_t i = 0; i < 9; i++){
     263           0 :         invOut[i] = inv[i];
     264             :     }
     265             : 
     266           0 :     return true;
     267             : }
     268             : 
     269             : /*
     270             :  *    fast matrix inverse code only for 4x4 square matrix copied from
     271             :  *    gluInvertMatrix implementation in opengl for 4x4 matrices.
     272             :  *
     273             :  *    @param     m,           input 4x4 matrix
     274             :  *    @param     invOut,      Output inverted 4x4 matrix
     275             :  *    @returns                false = matrix is Singular, true = matrix inversion successful
     276             :  */
     277             : template<typename T>
     278        1033 : static bool inverse4x4(const T m[],T invOut[])
     279             : {
     280             :     T inv[16], det;
     281             :     uint16_t i;
     282             : 
     283             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
     284             :     //disable FE_INEXACT detection as it fails on mac os runs
     285        1033 :     int old = fedisableexcept(FE_INEXACT | FE_OVERFLOW);
     286             :     if (old < 0) {
     287             :         // hal.console->printf("inverse4x4(): warning: error on disabling FE_OVERFLOW floating point exception\n");
     288             :     }
     289             : #endif
     290             : 
     291        1033 :     inv[0] = m[5]  * m[10] * m[15] -
     292        1033 :     m[5]  * m[11] * m[14] -
     293        1033 :     m[9]  * m[6]  * m[15] +
     294        1033 :     m[9]  * m[7]  * m[14] +
     295        1033 :     m[13] * m[6]  * m[11] -
     296        1033 :     m[13] * m[7]  * m[10];
     297             : 
     298        1033 :     inv[4] = -m[4]  * m[10] * m[15] +
     299        1033 :     m[4]  * m[11] * m[14] +
     300        1033 :     m[8]  * m[6]  * m[15] -
     301        1033 :     m[8]  * m[7]  * m[14] -
     302        1033 :     m[12] * m[6]  * m[11] +
     303        1033 :     m[12] * m[7]  * m[10];
     304             : 
     305        1033 :     inv[8] = m[4]  * m[9] * m[15] -
     306        1033 :     m[4]  * m[11] * m[13] -
     307        1033 :     m[8]  * m[5] * m[15] +
     308        1033 :     m[8]  * m[7] * m[13] +
     309        1033 :     m[12] * m[5] * m[11] -
     310        1033 :     m[12] * m[7] * m[9];
     311             : 
     312        1033 :     inv[12] = -m[4]  * m[9] * m[14] +
     313        1033 :     m[4]  * m[10] * m[13] +
     314        1033 :     m[8]  * m[5] * m[14] -
     315        1033 :     m[8]  * m[6] * m[13] -
     316        1033 :     m[12] * m[5] * m[10] +
     317        1033 :     m[12] * m[6] * m[9];
     318             : 
     319        1033 :     inv[1] = -m[1]  * m[10] * m[15] +
     320        1033 :     m[1]  * m[11] * m[14] +
     321        1033 :     m[9]  * m[2] * m[15] -
     322        1033 :     m[9]  * m[3] * m[14] -
     323        1033 :     m[13] * m[2] * m[11] +
     324        1033 :     m[13] * m[3] * m[10];
     325             : 
     326        1033 :     inv[5] = m[0]  * m[10] * m[15] -
     327        1033 :     m[0]  * m[11] * m[14] -
     328        1033 :     m[8]  * m[2] * m[15] +
     329        1033 :     m[8]  * m[3] * m[14] +
     330        1033 :     m[12] * m[2] * m[11] -
     331        1033 :     m[12] * m[3] * m[10];
     332             : 
     333        1033 :     inv[9] = -m[0]  * m[9] * m[15] +
     334        1033 :     m[0]  * m[11] * m[13] +
     335        1033 :     m[8]  * m[1] * m[15] -
     336        1033 :     m[8]  * m[3] * m[13] -
     337        1033 :     m[12] * m[1] * m[11] +
     338        1033 :     m[12] * m[3] * m[9];
     339             : 
     340        1033 :     inv[13] = m[0]  * m[9] * m[14] -
     341        1033 :     m[0]  * m[10] * m[13] -
     342        1033 :     m[8]  * m[1] * m[14] +
     343        1033 :     m[8]  * m[2] * m[13] +
     344        1033 :     m[12] * m[1] * m[10] -
     345        1033 :     m[12] * m[2] * m[9];
     346             : 
     347        1033 :     inv[2] = m[1]  * m[6] * m[15] -
     348        1033 :     m[1]  * m[7] * m[14] -
     349        1033 :     m[5]  * m[2] * m[15] +
     350        1033 :     m[5]  * m[3] * m[14] +
     351        1033 :     m[13] * m[2] * m[7] -
     352        1033 :     m[13] * m[3] * m[6];
     353             : 
     354        1033 :     inv[6] = -m[0]  * m[6] * m[15] +
     355        1033 :     m[0]  * m[7] * m[14] +
     356        1033 :     m[4]  * m[2] * m[15] -
     357        1033 :     m[4]  * m[3] * m[14] -
     358        1033 :     m[12] * m[2] * m[7] +
     359        1033 :     m[12] * m[3] * m[6];
     360             : 
     361        1033 :     inv[10] = m[0]  * m[5] * m[15] -
     362        1033 :     m[0]  * m[7] * m[13] -
     363        1033 :     m[4]  * m[1] * m[15] +
     364        1033 :     m[4]  * m[3] * m[13] +
     365        1033 :     m[12] * m[1] * m[7] -
     366        1033 :     m[12] * m[3] * m[5];
     367             : 
     368        1033 :     inv[14] = -m[0]  * m[5] * m[14] +
     369        1033 :     m[0]  * m[6] * m[13] +
     370        1033 :     m[4]  * m[1] * m[14] -
     371        1033 :     m[4]  * m[2] * m[13] -
     372        1033 :     m[12] * m[1] * m[6] +
     373        1033 :     m[12] * m[2] * m[5];
     374             : 
     375        1033 :     inv[3] = -m[1] * m[6] * m[11] +
     376        1033 :     m[1] * m[7] * m[10] +
     377        1033 :     m[5] * m[2] * m[11] -
     378        1033 :     m[5] * m[3] * m[10] -
     379        1033 :     m[9] * m[2] * m[7] +
     380        1033 :     m[9] * m[3] * m[6];
     381             : 
     382        1033 :     inv[7] = m[0] * m[6] * m[11] -
     383        1033 :     m[0] * m[7] * m[10] -
     384        1033 :     m[4] * m[2] * m[11] +
     385        1033 :     m[4] * m[3] * m[10] +
     386        1033 :     m[8] * m[2] * m[7] -
     387        1033 :     m[8] * m[3] * m[6];
     388             : 
     389        1033 :     inv[11] = -m[0] * m[5] * m[11] +
     390        1033 :     m[0] * m[7] * m[9] +
     391        1033 :     m[4] * m[1] * m[11] -
     392        1033 :     m[4] * m[3] * m[9] -
     393        1033 :     m[8] * m[1] * m[7] +
     394        1033 :     m[8] * m[3] * m[5];
     395             : 
     396        1033 :     inv[15] = m[0] * m[5] * m[10] -
     397        1033 :     m[0] * m[6] * m[9] -
     398        1033 :     m[4] * m[1] * m[10] +
     399        1033 :     m[4] * m[2] * m[9] +
     400        1033 :     m[8] * m[1] * m[6] -
     401        1033 :     m[8] * m[2] * m[5];
     402             : 
     403        1033 :     det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
     404             : 
     405        1033 :     if (is_zero(det) || isinf(det)){
     406          65 :         return false;
     407             :     }
     408             : 
     409         968 :     det = 1.0f / det;
     410             : 
     411       16456 :     for (i = 0; i < 16; i++)
     412       15488 :         invOut[i] = inv[i] * det;
     413             :     
     414             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
     415         968 :     if (old >= 0 && feenableexcept(old) < 0) {
     416             :         // hal.console->printf("inverse4x4(): warning: error on restoring floating exception mask\n");
     417             :     }
     418             : #endif
     419             : 
     420         968 :     return true;
     421             : }
     422             : 
     423             : /*
     424             :  *    generic matrix inverse code
     425             :  *
     426             :  *    @param     x,     input nxn matrix
     427             :  *    @param     y,     Output inverted nxn matrix
     428             :  *    @param     n,     dimension of square matrix
     429             :  *    @returns          false = matrix is Singular, true = matrix inversion successful
     430             :  */
     431             : template<typename T>
     432        1973 : bool mat_inverse(const T x[], T y[], uint16_t dim)
     433             : {
     434        1973 :     switch(dim){
     435           0 :     case 3: return inverse3x3(x,y);
     436        1033 :     case 4: return inverse4x4(x,y);
     437         940 :     default: return mat_inverseN(x,y,dim);
     438             :     }
     439             : }
     440             : 
     441             : template <typename T>
     442           0 : void mat_mul(const T *A, const T *B, T *C, uint16_t n)
     443             : {
     444           0 :     memset(C, 0, sizeof(T)*n*n);
     445           0 :     for(uint16_t i = 0; i < n; i++) {
     446           0 :         for(uint16_t j = 0; j < n; j++) {
     447           0 :             for(uint16_t k = 0;k < n; k++) {
     448           0 :                 C[i*n + j] += A[i*n + k] * B[k*n + j];
     449             :             }
     450             :         }
     451             :     }
     452           0 : }
     453             : 
     454             : template <typename T>
     455           0 : void mat_identity(T *A, uint16_t n)
     456             : {
     457           0 :     memset(A, 0, sizeof(T)*n*n);
     458           0 :     for (uint16_t i=0; i<n; i++) {
     459           0 :         A[i*n+i] = 1;
     460             :     }
     461           0 : }
     462             : 
     463             : template bool mat_inverse<float>(const float x[], float y[], uint16_t dim);
     464             : template void mat_mul<float>(const float *A, const float *B, float *C, uint16_t n);
     465             : template void mat_identity<float>(float x[], uint16_t dim);
     466             : 
     467             : template bool mat_inverse<double>(const double x[], double y[], uint16_t dim);
     468             : template void mat_mul<double>(const double *A, const double *B, double *C, uint16_t n);
     469             : template void mat_identity<double>(double x[], uint16_t dim);

Generated by: LCOV version 1.14