blob: ba7486ddf4498565459a5da554c100975bd22f0e [file] [log] [blame]
Austin Schuh3333ec72022-12-29 16:21:06 -08001/* Copyright (C) 2013-2016, The Regents of The University of Michigan.
2All rights reserved.
3This software was developed in the APRIL Robotics Lab under the
4direction of Edwin Olson, ebolson@umich.edu. This software may be
5available under alternative licensing terms; contact the address above.
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are met:
81. Redistributions of source code must retain the above copyright notice, this
9 list of conditions and the following disclaimer.
102. Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation
12 and/or other materials provided with the distribution.
13THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23The views and conclusions contained in the software and documentation are those
24of the authors and should not be interpreted as representing official policies,
25either expressed or implied, of the Regents of The University of Michigan.
26*/
27
28#include <math.h>
29
30/** SVD 2x2.
31
32 Computes singular values and vectors without squaring the input
33 matrix. With double precision math, results are accurate to about
34 1E-16.
35
36 U = [ cos(theta) -sin(theta) ]
37 [ sin(theta) cos(theta) ]
38
39 S = [ e 0 ]
40 [ 0 f ]
41
42 V = [ cos(phi) -sin(phi) ]
43 [ sin(phi) cos(phi) ]
44
45
46 Our strategy is basically to analytically multiply everything out
47 and then rearrange so that we can solve for theta, phi, e, and
48 f. (Derivation by ebolson@umich.edu 5/2016)
49
50 V' = [ CP SP ]
51 [ -SP CP ]
52
53USV' = [ CT -ST ][ e*CP e*SP ]
54 [ ST CT ][ -f*SP f*CP ]
55
56 = [e*CT*CP + f*ST*SP e*CT*SP - f*ST*CP ]
57 [e*ST*CP - f*SP*CT e*SP*ST + f*CP*CT ]
58
59A00+A11 = e*CT*CP + f*ST*SP + e*SP*ST + f*CP*CT
60 = e*(CP*CT + SP*ST) + f*(SP*ST + CP*CT)
61 = (e+f)(CP*CT + SP*ST)
62B0 = (e+f)*cos(P-T)
63
64A00-A11 = e*CT*CP + f*ST*SP - e*SP*ST - f*CP*CT
65 = e*(CP*CT - SP*ST) - f*(-ST*SP + CP*CT)
66 = (e-f)(CP*CT - SP*ST)
67B1 = (e-f)*cos(P+T)
68
69A01+A10 = e*CT*SP - f*ST*CP + e*ST*CP - f*SP*CT
70 = e(CT*SP + ST*CP) - f*(ST*CP + SP*CT)
71 = (e-f)*(CT*SP + ST*CP)
72B2 = (e-f)*sin(P+T)
73
74A01-A10 = e*CT*SP - f*ST*CP - e*ST*CP + f*SP*CT
75 = e*(CT*SP - ST*CP) + f(SP*CT - ST*CP)
76 = (e+f)*(CT*SP - ST*CP)
77B3 = (e+f)*sin(P-T)
78
79B0 = (e+f)*cos(P-T)
80B1 = (e-f)*cos(P+T)
81B2 = (e-f)*sin(P+T)
82B3 = (e+f)*sin(P-T)
83
84B3/B0 = tan(P-T)
85
86B2/B1 = tan(P+T)
87 **/
88void svd22(const double A[4], double U[4], double S[2], double V[4])
89{
90 double A00 = A[0];
91 double A01 = A[1];
92 double A10 = A[2];
93 double A11 = A[3];
94
95 double B0 = A00 + A11;
96 double B1 = A00 - A11;
97 double B2 = A01 + A10;
98 double B3 = A01 - A10;
99
100 double PminusT = atan2(B3, B0);
101 double PplusT = atan2(B2, B1);
102
103 double P = (PminusT + PplusT) / 2;
104 double T = (-PminusT + PplusT) / 2;
105
106 double CP = cos(P), SP = sin(P);
107 double CT = cos(T), ST = sin(T);
108
109 U[0] = CT;
110 U[1] = -ST;
111 U[2] = ST;
112 U[3] = CT;
113
114 V[0] = CP;
115 V[1] = -SP;
116 V[2] = SP;
117 V[3] = CP;
118
119 // C0 = e+f. There are two ways to compute C0; we pick the one
120 // that is better conditioned.
121 double CPmT = cos(P-T), SPmT = sin(P-T);
122 double C0 = 0;
123 if (fabs(CPmT) > fabs(SPmT))
124 C0 = B0 / CPmT;
125 else
126 C0 = B3 / SPmT;
127
128 // C1 = e-f. There are two ways to compute C1; we pick the one
129 // that is better conditioned.
130 double CPpT = cos(P+T), SPpT = sin(P+T);
131 double C1 = 0;
132 if (fabs(CPpT) > fabs(SPpT))
133 C1 = B1 / CPpT;
134 else
135 C1 = B2 / SPpT;
136
137 // e and f are the singular values
138 double e = (C0 + C1) / 2;
139 double f = (C0 - C1) / 2;
140
141 if (e < 0) {
142 e = -e;
143 U[0] = -U[0];
144 U[2] = -U[2];
145 }
146
147 if (f < 0) {
148 f = -f;
149 U[1] = -U[1];
150 U[3] = -U[3];
151 }
152
153 // sort singular values.
154 if (e > f) {
155 // already in big-to-small order.
156 S[0] = e;
157 S[1] = f;
158 } else {
159 // Curiously, this code never seems to get invoked. Why is it
160 // that S[0] always ends up the dominant vector? However,
161 // this code has been tested (flipping the logic forces us to
162 // sort the singular values in ascending order).
163 //
164 // P = [ 0 1 ; 1 0 ]
165 // USV' = (UP)(PSP)(PV')
166 // = (UP)(PSP)(VP)'
167 // = (UP)(PSP)(P'V')'
168 S[0] = f;
169 S[1] = e;
170
171 // exchange columns of U and V
172 double tmp[2];
173 tmp[0] = U[0];
174 tmp[1] = U[2];
175 U[0] = U[1];
176 U[2] = U[3];
177 U[1] = tmp[0];
178 U[3] = tmp[1];
179
180 tmp[0] = V[0];
181 tmp[1] = V[2];
182 V[0] = V[1];
183 V[2] = V[3];
184 V[1] = tmp[0];
185 V[3] = tmp[1];
186 }
187
188 /*
189 double SM[4] = { S[0], 0, 0, S[1] };
190
191 doubles_print_mat(U, 2, 2, "%20.10g");
192 doubles_print_mat(SM, 2, 2, "%20.10g");
193 doubles_print_mat(V, 2, 2, "%20.10g");
194 printf("A:\n");
195 doubles_print_mat(A, 2, 2, "%20.10g");
196
197 double SVt[4];
198 doubles_mat_ABt(SM, 2, 2, V, 2, 2, SVt, 2, 2);
199 double USVt[4];
200 doubles_mat_AB(U, 2, 2, SVt, 2, 2, USVt, 2, 2);
201
202 printf("USVt\n");
203 doubles_print_mat(USVt, 2, 2, "%20.10g");
204
205 double diff[4];
206 for (int i = 0; i < 4; i++)
207 diff[i] = A[i] - USVt[i];
208
209 printf("diff\n");
210 doubles_print_mat(diff, 2, 2, "%20.10g");
211
212 */
213
214}
215
216
217// for the matrix [a b; b d]
218void svd_sym_singular_values(double A00, double A01, double A11,
219 double *Lmin, double *Lmax)
220{
221 double A10 = A01;
222
223 double B0 = A00 + A11;
224 double B1 = A00 - A11;
225 double B2 = A01 + A10;
226 double B3 = A01 - A10;
227
228 double PminusT = atan2(B3, B0);
229 double PplusT = atan2(B2, B1);
230
231 double P = (PminusT + PplusT) / 2;
232 double T = (-PminusT + PplusT) / 2;
233
234 // C0 = e+f. There are two ways to compute C0; we pick the one
235 // that is better conditioned.
236 double CPmT = cos(P-T), SPmT = sin(P-T);
237 double C0 = 0;
238 if (fabs(CPmT) > fabs(SPmT))
239 C0 = B0 / CPmT;
240 else
241 C0 = B3 / SPmT;
242
243 // C1 = e-f. There are two ways to compute C1; we pick the one
244 // that is better conditioned.
245 double CPpT = cos(P+T), SPpT = sin(P+T);
246 double C1 = 0;
247 if (fabs(CPpT) > fabs(SPpT))
248 C1 = B1 / CPpT;
249 else
250 C1 = B2 / SPpT;
251
252 // e and f are the singular values
253 double e = (C0 + C1) / 2;
254 double f = (C0 - C1) / 2;
255
256 *Lmin = fmin(e, f);
257 *Lmax = fmax(e, f);
258}