Qu'est-ce qu'un algorithme simple pour calculer la SVD de matrices?
Idéalement, j'aimerais un algorithme numériquement robuste, mais j'aimerais voir à la fois des implémentations simples et pas si simples. Code C accepté.
Des références à des articles ou à du code?
Réponses:
Voir /math/861674/decompose-a-2d-arbitrary-transform-into-only-scaling-and-rotation (désolé, j'aurais mis cela dans un commentaire mais je me suis inscrit juste pour poster ceci donc je ne peux pas encore poster de commentaires).
Mais comme je l'écris comme réponse, j'écrirai également la méthode:
Cela décompose la matrice comme suit:
La seule chose à éviter avec cette méthode est queG=F=0 ou H=E=0 pour atan2.
Je doute qu'il puisse être plus robuste que ça( Mise à jour: voir la réponse d'Alex Eftimiades!).La référence est: http://dx.doi.org/10.1109/38.486688 (donnée par Rahul là-bas) qui vient du bas de cet article de blog: http://metamerist.blogspot.com/2006/10/linear-algebra -for-graphics-geeks-svd.html
Mise à jour: Comme indiqué par @VictorLiu dans un commentaire,sy peut être négatif. Cela se produit si et seulement si le déterminant de la matrice d'entrée est également négatif. Si c'est le cas et que vous voulez les valeurs singulières positives, prenez simplement la valeur absolue de sy .
la source
@Pedro Gimeno
"Je doute qu'il puisse être plus robuste que cela."
Défi accepté.
J'ai remarqué que l'approche habituelle consiste à utiliser des fonctions trigonométriques comme atan2. Intuitivement, il ne devrait pas être nécessaire d'utiliser des fonctions trigonométriques. En effet, tous les résultats se retrouvent sous forme de sinus et cosinus d'arctans - ce qui peut être simplifié en fonctions algébriques. Cela a pris un certain temps, mais j'ai réussi à simplifier l'algorithme de Pedro pour utiliser uniquement les fonctions algébriques.
Le code python suivant fait l'affaire.
la source
y1
= 0,x1
= 0,h1
= 0 ett1
= 0/0 =NaN
.Le GSL a un solveur SVD 2 par 2 sous-jacent à la partie de décomposition QR de l'algorithme SVD principal pour
gsl_linalg_SV_decomp
. Consultez lesvdstep.c
fichier et recherchez lasvd2
fonction. La fonction a quelques cas particuliers, n'est pas vraiment triviale et semble faire plusieurs choses pour être prudent numériquement (par exemple, utiliserhypot
pour éviter les débordements).la source
ChangeLog
fichier si vous téléchargez le GSL. Et vous pouvez consulter lessvd.c
détails de l'algorithme global. La seule vraie documentation semble concerner les fonctions de haut niveau appelables par l'utilisateur, par exemplegsl_linalg_SV_decomp
.Lorsque nous disons "numériquement robuste", nous entendons généralement un algorithme dans lequel nous faisons des choses comme le pivotement pour éviter la propagation des erreurs. Cependant, pour une matrice 2x2, vous pouvez écrire le résultat en termes de formules explicites - c'est-à-dire, écrire des formules pour les éléments SVD qui énoncent le résultat uniquement en termes d'entrées , plutôt qu'en termes de valeurs intermédiaires précédemment calculées . Cela signifie que vous pouvez avoir une annulation mais pas de propagation d'erreur.
Le fait est simplement que pour les systèmes 2x2, il n'est pas nécessaire de se soucier de la robustesse.
la source
Ce code est basé sur le papier de Blinn , papier Ellis , conférence SVD , et d' autres calculs. Un algorithme convient aux matrices réelles régulières et singulières. Toutes les versions précédentes fonctionnent à 100% ainsi que celle-ci.
la source
J'avais besoin d'un algorithme qui a
Nous voulons calculerc1, s1, c2, s2, σ1 et σ2 comme suit:
The main idea is to find a rotation matrixV that diagonalizes ATA , that is VATAVT=D is diagonal.
Recall that
Multiplying both sides byS−1 we get
SinceD is diagonal, setting S to D−−√ will give us UTU=Identity , meaning U is a rotation matrix, S is a diagonal matrix, V is a rotation matrix and USV=A , just what we are looking for.
Calculating the diagonalizing rotation can be done by solving the following equation:
where
andt2 is the tangent of angle of V . This can be derived by expanding VATAVT and making its off-diagonal elements equal to zero (they are equal to each other).
The problem with this method is that it loses significant floating point precision when calculatingβ−α and γ for certain matrices, because of the subtractions in the calculations. The solution for this is to do an RQ decomposition (A=RQ , R upper triangular and Q orthogonal) first, then use the algorithm to factorize USV′=R . This gives USV=USV′Q=RQ=A . Notice how setting d to 0 (as in R ) eliminates some of the additions/subtractions. (The RQ decomposition is fairly trivial from the expansion of the matrix product).
The algorithm naively implemented this way has some numerical and logical anomalies (e.g. isS + D--√ ou - D--√ ), que j'ai corrigé dans le code ci-dessous.
J'ai jeté environ 2000 millions de matrices randomisées sur le code, et la plus grande erreur numérique produite était d'environ6 ⋅ 10- 7 (avec flotteurs 32 bits, e r r o r = | | USV- M| | / | | M| | ). L'algorithme s'exécute sur environ 340 cycles d'horloge (MSVC 19, Ivy Bridge).
Idées de:
http://www.cs.utexas.edu/users/inderjit/public_papers/HLA_SVD.pdf
http://www.math.pitt.edu/~sussmanm/2071Spring08/lab09/index.html
http: // www.lucidarme.me/singular-value-decomposition-of-a-2x2-matrix/
la source
J'ai utilisé la description à http://www.lucidarme.me/?p=4624 pour créer ce code C ++. Les matrices sont celles de la bibliothèque Eigen, mais vous pouvez facilement créer votre propre structure de données à partir de cet exemple:
Avec la fonction de signe standard
Il en résulte exactement les mêmes valeurs que le
Eigen::JacobiSVD
(voir https://eigen.tuxfamily.org/dox-devel/classEigen_1_1JacobiSVD.html ).la source
S2 = hypot( a*a + b*b - c*c - d*d, 2*(a*c + b*d))
J'ai du code C pur pour le vrai SVD 2x2 ici . Voir ligne 559. Il calcule essentiellement les valeurs propres deUNETUNE en résolvant un quadratique, ce n'est donc pas forcément le plus robuste, mais il semble bien fonctionner en pratique pour les cas pas trop pathologiques. C'est relativement simple.
la source
Pour mes besoins personnels, j'ai essayé d'isoler le calcul minimum pour un svd 2x2. Je suppose que c'est probablement l'une des solutions les plus simples et les plus rapides. Vous pouvez trouver des détails sur mon blog personnel: http://lucidarme.me/?p=4624 .
Avantages: simple, rapide et vous ne pouvez calculer qu'une ou deux des trois matrices (S, U ou D) si vous n'avez pas besoin des trois matrices.
Inconvénient, il utilise atan2, qui peut être inexact et peut nécessiter une bibliothèque externe (typ. Math.h).
la source
Voici une implémentation d'une résolution SVD 2x2. Je l'ai basé sur le code de Victor Liu. Son code ne fonctionnait pas pour certaines matrices. J'ai utilisé ces deux documents comme référence mathématique pour la résolution: pdf1 et pdf2 .
La
setData
méthode matricielle est dans l'ordre des lignes principales. En interne, je représente les données de la matrice comme un tableau 2D donné pardata[col][row]
.la source