| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
/* From bryant@sioux.stanford.edu Sat Apr 3 14:57:54 1993 |
|
2
|
|
|
|
|
|
|
Return-Path: |
|
3
|
|
|
|
|
|
|
Received: from sioux.stanford.edu by alnitak.usc.edu (4.1/SMI-4.1+ucs-3.6) |
|
4
|
|
|
|
|
|
|
id AA12724; Sat, 3 Apr 93 14:57:52 PST |
|
5
|
|
|
|
|
|
|
Received: from oglala.ice (oglala.Stanford.EDU) by sioux.stanford.edu (4.1/inc-1.0) |
|
6
|
|
|
|
|
|
|
id AA07300; Sat, 3 Apr 93 14:53:25 PST |
|
7
|
|
|
|
|
|
|
Date: Sat, 3 Apr 93 14:53:25 PST |
|
8
|
|
|
|
|
|
|
From: bryant@sioux.stanford.edu (Bryant Marks) |
|
9
|
|
|
|
|
|
|
Message-Id: <9304032253.AA07300@sioux.stanford.edu> |
|
10
|
|
|
|
|
|
|
To: ajayshah@rcf.usc.edu |
|
11
|
|
|
|
|
|
|
Subject: Re: SVD |
|
12
|
|
|
|
|
|
|
Status: ORr |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
> Hi! Long ago you sent me an svd routine in C based on code |
|
16
|
|
|
|
|
|
|
> from Nash in Pascal. Has this changed any over the years? (Your |
|
17
|
|
|
|
|
|
|
> email is dated July 1992). Is your code available by anon ftp? |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
Hi Ajay, |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
I don't think I have changed the code -- but here's my most recent |
|
22
|
|
|
|
|
|
|
version of the code, you can check to see if it's any different. |
|
23
|
|
|
|
|
|
|
Currently it's not available via anonymous ftp but feel free to |
|
24
|
|
|
|
|
|
|
redistribute the code -- it seems to work well in the application |
|
25
|
|
|
|
|
|
|
I'm using it in. |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
Bryant |
|
29
|
|
|
|
|
|
|
*/ |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
/* This SVD routine is based on pgs 30-48 of "Compact Numerical Methods |
|
32
|
|
|
|
|
|
|
for Computers" by J.C. Nash (1990), used to compute the pseudoinverse. |
|
33
|
|
|
|
|
|
|
Modifications include: |
|
34
|
|
|
|
|
|
|
Translation from Pascal to ANSI C. |
|
35
|
|
|
|
|
|
|
Array indexing from 0 rather than 1. |
|
36
|
|
|
|
|
|
|
Float replaced by double everywhere. |
|
37
|
|
|
|
|
|
|
Support for the Matrix structure. |
|
38
|
|
|
|
|
|
|
I changed the array indexing so that the matricies (float [][]) |
|
39
|
|
|
|
|
|
|
could be replaced be a single list (double *) for more |
|
40
|
|
|
|
|
|
|
efficient communication with Mathematica. |
|
41
|
|
|
|
|
|
|
*/ |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
/* rjrw 7/7/99: changed z back to a vector, moved one line... */ |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
/* |
|
46
|
|
|
|
|
|
|
Derek A. Lamb 2016: added comments to aid understanding, since |
|
47
|
|
|
|
|
|
|
Nash's book will only get more difficult to find in the future. |
|
48
|
|
|
|
|
|
|
*/ |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
/* |
|
51
|
|
|
|
|
|
|
Form a singular value decomposition of matrix A which is stored in |
|
52
|
|
|
|
|
|
|
the first nRow*nCol elements of working array W. Upon return, the |
|
53
|
|
|
|
|
|
|
first nRow*nCol elements of W will become the product U x S of a |
|
54
|
|
|
|
|
|
|
thin svd, where S is the diagonal (rectangular) matrix of singular |
|
55
|
|
|
|
|
|
|
values. The last nCol*nCol elements of W will be the square matrix |
|
56
|
|
|
|
|
|
|
V of a thin svd. On return, Z will contain the squares of the |
|
57
|
|
|
|
|
|
|
singular values. |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
The input matrix A is assumed to have nRows >= nCol. If it does |
|
60
|
|
|
|
|
|
|
not, one should input the transpose of A, A", to find the the svd |
|
61
|
|
|
|
|
|
|
of A, since A = U x S x V" and A" = V x S x U". (The " means |
|
62
|
|
|
|
|
|
|
transpose.) |
|
63
|
|
|
|
|
|
|
*/ |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
#include |
|
66
|
|
|
|
|
|
|
#define TOLERANCE 1.0e-22 |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
#ifdef MAIN |
|
69
|
|
|
|
|
|
|
#include |
|
70
|
|
|
|
|
|
|
#define NC 2 |
|
71
|
|
|
|
|
|
|
#define NR 2 |
|
72
|
|
|
|
|
|
|
int main() |
|
73
|
|
|
|
|
|
|
{ |
|
74
|
|
|
|
|
|
|
int i,j,n,m; |
|
75
|
|
|
|
|
|
|
double w[NC*(NR+NC)], z[NC*NC]; |
|
76
|
|
|
|
|
|
|
void SVD(double *W, double *Z, int nRow, int nCol); |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
for (i=0;i
|
|
79
|
|
|
|
|
|
|
w[i] = 0.; |
|
80
|
|
|
|
|
|
|
} |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
for (i=0;i
|
|
83
|
|
|
|
|
|
|
z[i] = 0.; |
|
84
|
|
|
|
|
|
|
} |
|
85
|
|
|
|
|
|
|
w[0] = 1; w[1] = 3; w[NC] = -4; w[NC+1] = 3; |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
SVD(w, z, NR, NC); |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
printf("W:\n"); |
|
90
|
|
|
|
|
|
|
for (i=0;i
|
|
91
|
|
|
|
|
|
|
printf("%d %g\n",i,w[i]); |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
printf("\nZ:\n"); |
|
94
|
|
|
|
|
|
|
for (i=0;i
|
|
95
|
|
|
|
|
|
|
printf("%d %g\n",i,z[i]); |
|
96
|
|
|
|
|
|
|
} |
|
97
|
|
|
|
|
|
|
return 0; |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
#endif |
|
100
|
|
|
|
|
|
|
|
|
101
|
23
|
|
|
|
|
|
void SVD(double *W, double *Z, int nRow, int nCol) |
|
102
|
|
|
|
|
|
|
{ |
|
103
|
|
|
|
|
|
|
int i, j, k, EstColRank, RotCount, SweepCount, slimit; |
|
104
|
|
|
|
|
|
|
double eps, e2, tol, vt, p, x0, y0, q, r, c0, s0, d1, d2; |
|
105
|
23
|
|
|
|
|
|
eps = TOLERANCE; |
|
106
|
|
|
|
|
|
|
/*set a limit on the number of sweeps allowed. A suggested limit is slimit=max([nCol/4],6).*/ |
|
107
|
23
|
|
|
|
|
|
slimit = nCol/4; |
|
108
|
23
|
50
|
|
|
|
|
if (slimit < 6.0) |
|
109
|
23
|
|
|
|
|
|
slimit = 6; |
|
110
|
23
|
|
|
|
|
|
SweepCount = 0; |
|
111
|
23
|
|
|
|
|
|
e2 = 10.0*nRow*eps*eps; |
|
112
|
23
|
|
|
|
|
|
tol = eps*.1; /*set convergence tolerances */ |
|
113
|
23
|
|
|
|
|
|
EstColRank = nCol; /* current estimate of rank */ |
|
114
|
|
|
|
|
|
|
/* Set V matrix to the unit matrix of order nCol. |
|
115
|
|
|
|
|
|
|
V is stored in elements nCol*nRow to nCol*(nRow+nCol)-1 of array W. */ |
|
116
|
142
|
100
|
|
|
|
|
for (i=0; i
|
|
117
|
1122
|
100
|
|
|
|
|
for (j=0; j
|
|
118
|
1003
|
|
|
|
|
|
W[nCol*(nRow+i)+j] = 0.0; |
|
119
|
|
|
|
|
|
|
} |
|
120
|
119
|
|
|
|
|
|
W[nCol*(nRow+i)+i] = 1.0; /* rjrw 7/7/99: moved this line out of j loop */ |
|
121
|
|
|
|
|
|
|
} |
|
122
|
23
|
|
|
|
|
|
RotCount = EstColRank*(EstColRank-1)/2; |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
/*until convergence is achieved or too many sweeps are carried out*/ |
|
125
|
157
|
100
|
|
|
|
|
while (RotCount != 0 && SweepCount <= slimit) |
|
|
|
100
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
{ |
|
127
|
134
|
|
|
|
|
|
RotCount = EstColRank*(EstColRank-1)/2; /* rotation counter */ |
|
128
|
134
|
|
|
|
|
|
SweepCount++; |
|
129
|
773
|
100
|
|
|
|
|
for (j=0; j
|
|
130
|
|
|
|
|
|
|
{ |
|
131
|
3658
|
100
|
|
|
|
|
for (k=j+1; k
|
|
132
|
|
|
|
|
|
|
{ |
|
133
|
3019
|
|
|
|
|
|
p = q = r = 0.0; |
|
134
|
|
|
|
|
|
|
/* |
|
135
|
|
|
|
|
|
|
Some helpful definitions from Nash's book (pg 34) to |
|
136
|
|
|
|
|
|
|
understand this p, q, & r rotation business: |
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
p = x"y; (" means transpose) |
|
139
|
|
|
|
|
|
|
q = x"x - y"y; |
|
140
|
|
|
|
|
|
|
v = sqrt(4*p^2+q^2); |
|
141
|
|
|
|
|
|
|
if (q>=0) { |
|
142
|
|
|
|
|
|
|
cos(phi) = sqrt((v+q)/(2*v)); |
|
143
|
|
|
|
|
|
|
sin(phi) = p/(v*cos(phi)); |
|
144
|
|
|
|
|
|
|
} else if (q<0) {, |
|
145
|
|
|
|
|
|
|
(sgn(p)=+1 for p>=0, -1 for p<0); |
|
146
|
|
|
|
|
|
|
sin(phi) = sgn(p)*sqrt((v-q)/(2*v)); |
|
147
|
|
|
|
|
|
|
cos(phi) = p/(v*sin(phi)); |
|
148
|
|
|
|
|
|
|
} |
|
149
|
|
|
|
|
|
|
This formulation avoids the subtraction of two nearly |
|
150
|
|
|
|
|
|
|
equal numbers, which is bound to happen to q and v as |
|
151
|
|
|
|
|
|
|
the matrix approaches orthogonality. |
|
152
|
|
|
|
|
|
|
*/ |
|
153
|
79826
|
100
|
|
|
|
|
for (i=0; i
|
|
154
|
|
|
|
|
|
|
{ |
|
155
|
76807
|
|
|
|
|
|
x0 = W[nCol*i+j]; y0 = W[nCol*i+k]; |
|
156
|
76807
|
|
|
|
|
|
p += x0*y0; q += x0*x0; r += y0*y0; |
|
157
|
|
|
|
|
|
|
} |
|
158
|
3019
|
|
|
|
|
|
Z[j] = q; Z[k] = r; |
|
159
|
|
|
|
|
|
|
/* Convergence test: will rotation exchange order of columns?*/ |
|
160
|
3019
|
100
|
|
|
|
|
if (q >= r) /* check if columns are ordered */ |
|
161
|
|
|
|
|
|
|
{ /* columns are ordered, so try convergence test */ |
|
162
|
2643
|
50
|
|
|
|
|
if (q<=e2*Z[0] || fabs(p)<=tol*q) RotCount--; |
|
|
|
100
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
/* There is no more work on this particular pair of |
|
164
|
|
|
|
|
|
|
columns in the current sweep. The first condition |
|
165
|
|
|
|
|
|
|
checks for very small column norms in BOTH |
|
166
|
|
|
|
|
|
|
columns, for which no rotation makes sense. The |
|
167
|
|
|
|
|
|
|
second condition determines if the inner product |
|
168
|
|
|
|
|
|
|
is small with respect to the larger of the |
|
169
|
|
|
|
|
|
|
columns, which implies a very small rotation |
|
170
|
|
|
|
|
|
|
angle. */ |
|
171
|
|
|
|
|
|
|
else |
|
172
|
|
|
|
|
|
|
{/* columns are in order, but their inner product is not small */ |
|
173
|
2328
|
|
|
|
|
|
p /= q; r = 1 - r/q; vt = sqrt(4*p*p+r*r); |
|
174
|
|
|
|
|
|
|
/* DAL thinks the fabs is unnecessary in the |
|
175
|
|
|
|
|
|
|
next line: vt is non-negative; q>=r, r/q <=1 |
|
176
|
|
|
|
|
|
|
so after the assignment 0<= r <=1, so r/vt is |
|
177
|
|
|
|
|
|
|
positive, so everything inside the sqrt |
|
178
|
|
|
|
|
|
|
should be positive even without the fabs. abs |
|
179
|
|
|
|
|
|
|
isn't in Nash's book. c0 and s0 are cos(phi) and sin(phi) as above for q>=0*/ |
|
180
|
2328
|
|
|
|
|
|
c0 = sqrt(fabs(.5*(1+r/vt))); s0 = p/(vt*c0); |
|
181
|
|
|
|
|
|
|
/* this little for loop (and the one below) is just rotation, inlined here for efficiency */ |
|
182
|
90792
|
100
|
|
|
|
|
for (i=0; i
|
|
183
|
|
|
|
|
|
|
{ |
|
184
|
88464
|
|
|
|
|
|
d1 = W[nCol*i+j]; d2 = W[nCol*i+k]; |
|
185
|
88464
|
|
|
|
|
|
W[nCol*i+j] = d1*c0+d2*s0; W[nCol*i+k] = -d1*s0+d2*c0; |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
} |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
else /* columns out of order -- must rotate */ |
|
190
|
|
|
|
|
|
|
{ /* note: r>q, and cannot be zero since both are sums |
|
191
|
|
|
|
|
|
|
of squares for the svd. In the case of a real |
|
192
|
|
|
|
|
|
|
symmetric matrix, this assumption must be |
|
193
|
|
|
|
|
|
|
questioned. */ |
|
194
|
376
|
|
|
|
|
|
p /= r; q = q/r-1; vt = sqrt(4*p*p+q*q); |
|
195
|
376
|
|
|
|
|
|
s0 = sqrt(fabs(.5*(1-q/vt))); |
|
196
|
|
|
|
|
|
|
/* DAL wondering about the fabs again, since after |
|
197
|
|
|
|
|
|
|
assignment q is <0 and vt is again positive, so |
|
198
|
|
|
|
|
|
|
everything inside the fabs should be positive |
|
199
|
|
|
|
|
|
|
already */ |
|
200
|
376
|
100
|
|
|
|
|
if (p<0) s0 = -s0; /*s0 and c0 are sin(phi) and cos(phi) as above for q<0 */ |
|
201
|
376
|
|
|
|
|
|
c0 = p/(vt*s0); |
|
202
|
14591
|
100
|
|
|
|
|
for (i=0; i
|
|
203
|
|
|
|
|
|
|
{ |
|
204
|
14215
|
|
|
|
|
|
d1 = W[nCol*i+j]; d2 = W[nCol*i+k]; |
|
205
|
14215
|
|
|
|
|
|
W[nCol*i+j] = d1*c0+d2*s0; W[nCol*i+k] = -d1*s0+d2*c0; |
|
206
|
|
|
|
|
|
|
} |
|
207
|
|
|
|
|
|
|
} |
|
208
|
|
|
|
|
|
|
/* Both angle calculations have been set up so that |
|
209
|
|
|
|
|
|
|
large numbers do not occur in intermediate |
|
210
|
|
|
|
|
|
|
quantities. This is easy in the svd case, since |
|
211
|
|
|
|
|
|
|
quantities x2,y2 cannot be negative.*/ |
|
212
|
|
|
|
|
|
|
} /* loop on k */ |
|
213
|
|
|
|
|
|
|
} /* loop on j */ |
|
214
|
136
|
100
|
|
|
|
|
while (EstColRank>=3 && Z[(EstColRank-1)]<=Z[0]*tol+tol*tol) |
|
|
|
100
|
|
|
|
|
|
|
215
|
2
|
|
|
|
|
|
EstColRank--; |
|
216
|
|
|
|
|
|
|
} |
|
217
|
|
|
|
|
|
|
#if DEBUG |
|
218
|
|
|
|
|
|
|
if (SweepCount > slimit) |
|
219
|
|
|
|
|
|
|
fprintf(stderr, "Sweeps = %d\n", SweepCount); |
|
220
|
|
|
|
|
|
|
#endif |
|
221
|
23
|
|
|
|
|
|
} |