ruby-svd 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +2 -0
- data/README +6 -0
- data/ext/extconf.rb +3 -0
- data/ext/nrutil.h +376 -0
- data/ext/svd.c +66 -0
- data/ext/svd.h +194 -0
- data/lib/ruby-svd.rb +3 -0
- data/lib/svd_matrix.rb +41 -0
- metadata +71 -0
data/LICENSE
ADDED
data/README
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
Singular Value Decomposition for Ruby with no dependency on GSL or LAPACK. Uses a C extension
|
2
|
+
to the Numerical Recipies C implementation of an SVD matrix decomposer. Also includes an
|
3
|
+
extension to the native Ruby Matrix class with a simple LSA method (decomposes the matrix,
|
4
|
+
transposes matrix V, diagonalises the S array into a matrix, then removes all but the two
|
5
|
+
leading terms in S to compress the original matrix to two dimensions).
|
6
|
+
|
data/ext/extconf.rb
ADDED
data/ext/nrutil.h
ADDED
@@ -0,0 +1,376 @@
|
|
1
|
+
/* nrutil.h */
|
2
|
+
#include <stdio.h>
|
3
|
+
#include <stddef.h>
|
4
|
+
#include <stdlib.h>
|
5
|
+
#define NR_END 1
|
6
|
+
#define FREE_ARG char*
|
7
|
+
|
8
|
+
|
9
|
+
#ifndef _NR_UTILS_H_
|
10
|
+
#define _NR_UTILS_H_
|
11
|
+
|
12
|
+
static float sqrarg;
|
13
|
+
#define SQR(a) ((sqrarg = (a)) == 0.0 ? 0.0 : sqrarg*sqrarg)
|
14
|
+
|
15
|
+
static double dsqrarg;
|
16
|
+
#define DSQR(a) ((dsqrarg = (a)) == 0.0 ? 0.0 : dsqrarg*dsqrarg)
|
17
|
+
|
18
|
+
static double dmaxarg1, dmaxarg2;
|
19
|
+
#define DMAX(a, b) (dmaxarg1 = (a), dmaxarg2 = (b), (dmaxarg1) > (dmaxarg2) ? (dmaxarg1) : (dmaxarg2))
|
20
|
+
|
21
|
+
static double dminarg1, dminarg2;
|
22
|
+
#define DMIN(a, b) (dminarg1 = (a), dminarg2 = (b), (dminarg1) < (dminarg2) ? (dminarg1) : (dminarg2))
|
23
|
+
|
24
|
+
static float maxarg1, maxarg2;
|
25
|
+
#define FMAX(a, b) (maxarg1 = (a), maxarg2 = (b), (maxarg1) > (maxarg2) ? (maxarg1) : (maxarg2))
|
26
|
+
|
27
|
+
static float minarg1, minarg2;
|
28
|
+
#define FMIN(a, b) (minarg1 = (a), minarg2 = (b), (minarg1) < (minarg2) ? (minarg1) : (minarg2))
|
29
|
+
|
30
|
+
static long lmaxarg1, lmaxarg2;
|
31
|
+
#define LMAX(a, b) (lmaxarg1 = (a), lmaxarg2 = (b), (lmaxarg1) > (lmaxarg2) ? (lmaxarg1) : (lmaxarg2))
|
32
|
+
|
33
|
+
static long lminarg1, lminarg2;
|
34
|
+
#define LMIN(a, b) (lminarg1 = (a), lminarg2 = (b), (lminarg1) < (lminarg2) ? (lminarg1) : (lminarg2))
|
35
|
+
|
36
|
+
static int imaxarg1, imaxarg2;
|
37
|
+
#define IMAX(a, b) (imaxarg1 = (a), imaxarg2 = (b), (imaxarg1) > (imaxarg2) ? (imaxarg1) : (imaxarg2))
|
38
|
+
|
39
|
+
static int iminarg1, iminarg2;
|
40
|
+
#define IMIN(a, b) (iminarg1 = (a), iminarg2 = (b), (iminarg1) < (iminarg2) ? (iminarg1) : (iminarg2))
|
41
|
+
|
42
|
+
#define SIGN(a, b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
|
43
|
+
|
44
|
+
|
45
|
+
/* prototype declaration */
|
46
|
+
|
47
|
+
#if defined(__STDC__) || defined(ANSI) || defined(NRANSI) /* ANSI */
|
48
|
+
|
49
|
+
void nrerror(char error_test[]);
|
50
|
+
float *vector(long nl, long nh);
|
51
|
+
int *ivector(long nl, long nh);
|
52
|
+
unsigned char *cvector(long nl, long nh);
|
53
|
+
unsigned long *lvector(long nl, long nh);
|
54
|
+
double *dvector(long nl, long nh);
|
55
|
+
float **matrix(long nrl, long nrh, long ncl, long nch);
|
56
|
+
double **dmatrix(long nrl, long nrh, long ncl, long nch);
|
57
|
+
int **imatrix(long nrl, long nrh, long ncl, long nch);
|
58
|
+
float **submatrix(float **a, long oldrl, long oldrh,
|
59
|
+
long oldcl, long oldch, long newrl, long newcl);
|
60
|
+
float **convert_matrix(float *a, long nrl, long nrh, long ncl, long nch);
|
61
|
+
float ***f3tensor(long nrl, long nrh, long ncl, long nch,
|
62
|
+
long ndl, long ndh);
|
63
|
+
void free_vector(float *v, long nl, long nh);
|
64
|
+
void free_ivector(int *v, long nl, long nh);
|
65
|
+
void free_cvector(unsigned char *v, long nl, long nh);
|
66
|
+
void free_lvector(unsigned long *v, long nl, long nh);
|
67
|
+
void free_dvector(double *v, long nl, long nh);
|
68
|
+
void free_matrix(float **m, long nrl, long nrh, long ncl, long nch);
|
69
|
+
void free_dmatrix(double **m, long nrl, long nrh, long ncl, long nch);
|
70
|
+
void free_imatrix(int **m, long nrl, long nrh, long ncl, long nch);
|
71
|
+
void free_submatrix(float **b, long nrl, long nrh, long ncl, long nch);
|
72
|
+
void free_convert_matrix(float **b, long nrl, long nrh,
|
73
|
+
long ncl, long nch);
|
74
|
+
void free_f3tensor(float ***t, long nrl, long nrh, long ncl, long nch,
|
75
|
+
long ndl, long ndh);
|
76
|
+
|
77
|
+
#else /* ANSI */
|
78
|
+
/* traditional -K&R */
|
79
|
+
|
80
|
+
void nrerror();
|
81
|
+
float *vector();
|
82
|
+
|
83
|
+
#endif /* ANSI */
|
84
|
+
|
85
|
+
#endif /* _NR_UTILS_H_ */
|
86
|
+
|
87
|
+
|
88
|
+
/* function definition */
|
89
|
+
|
90
|
+
|
91
|
+
void nrerror(char error_text[])
|
92
|
+
/* Numerical Recipes standard error handler */
|
93
|
+
{
|
94
|
+
fprintf(stderr, "Numerical Recipes run-time error...\n");
|
95
|
+
fprintf(stderr, "%s\n",error_text);
|
96
|
+
fprintf(stderr, "...now exiting to system...\n");
|
97
|
+
exit(1);
|
98
|
+
}
|
99
|
+
|
100
|
+
float *vector(long nl, long nh)
|
101
|
+
/* allocate a float vector with subscript range v[nl..nr] */
|
102
|
+
{
|
103
|
+
float *v;
|
104
|
+
|
105
|
+
v = (float *)malloc((size_t)((nh - nl + 1 + NR_END) * sizeof(float)));
|
106
|
+
if (!v) nrerror("allocation failure in vector()");
|
107
|
+
return v - nl + NR_END;
|
108
|
+
}
|
109
|
+
|
110
|
+
int *ivector(long nl, long nh)
|
111
|
+
/* allocate an int vector with subscript range v[nl..nr] */
|
112
|
+
{
|
113
|
+
int *v;
|
114
|
+
|
115
|
+
v = (int *)malloc((size_t)((nh - nl + 1 + NR_END)*sizeof(int)));
|
116
|
+
if (!v) nrerror("allocation failure in ivector()");
|
117
|
+
return v - nl + NR_END;
|
118
|
+
}
|
119
|
+
|
120
|
+
unsigned char *cvector(long nl, long nh)
|
121
|
+
/* allocate an unsigned char vector with subscript range v[nl..nr] */
|
122
|
+
{
|
123
|
+
unsigned char *v;
|
124
|
+
|
125
|
+
v = (unsigned char *)malloc((size_t)((nh - nl + 1 + NR_END)
|
126
|
+
* sizeof(unsigned char)));
|
127
|
+
if (!v) nrerror("allocation failure in cvector()");
|
128
|
+
return v - nl + NR_END;
|
129
|
+
}
|
130
|
+
|
131
|
+
unsigned long *lvector(long nl, long nh)
|
132
|
+
/* allocate an unsigned long vector with subscript range v[nl..nr] */
|
133
|
+
{
|
134
|
+
unsigned long *v;
|
135
|
+
|
136
|
+
v = (unsigned long *)malloc((size_t)((nh - nl + 1 + NR_END) * sizeof(long)));
|
137
|
+
if (!v) nrerror("allocation failure in lvector()");
|
138
|
+
return v - nl + NR_END;
|
139
|
+
}
|
140
|
+
|
141
|
+
double *dvector(long nl, long nh)
|
142
|
+
/* allocate a double vector with subscript range v[nl..nr] */
|
143
|
+
{
|
144
|
+
double *v;
|
145
|
+
|
146
|
+
v = (double *)malloc((size_t)((nh - nl + 1 + NR_END) * sizeof(double)));
|
147
|
+
if (!v) nrerror("allocation failure in dvector()");
|
148
|
+
return v - nl + NR_END;
|
149
|
+
}
|
150
|
+
|
151
|
+
float **matrix(long nrl, long nrh, long ncl, long nch)
|
152
|
+
/* allocate a float matrix with subscript range m[nrl..nrh][ncl, nch] */
|
153
|
+
{
|
154
|
+
long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1;
|
155
|
+
float **m;
|
156
|
+
|
157
|
+
/* allocate pointers to rows */
|
158
|
+
m = (float **)malloc((size_t)((nrow + NR_END) * sizeof(float *)));
|
159
|
+
if (!m) nrerror("allocation failure 1 in matrix()");
|
160
|
+
m += NR_END;
|
161
|
+
m -= nrl;
|
162
|
+
|
163
|
+
/* allocate rows and set pointers to them */
|
164
|
+
m[nrl] = (float *)malloc((size_t)((nrow * ncol + NR_END) * sizeof(float)));
|
165
|
+
if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
|
166
|
+
m[nrl] += NR_END;
|
167
|
+
m[nrl] -= ncl;
|
168
|
+
|
169
|
+
for (i = nrl + 1; i <= nrh; i++) m[i] = m[i-1] + ncol;
|
170
|
+
|
171
|
+
/* return pointer to array of pointers to rows */
|
172
|
+
return m;
|
173
|
+
}
|
174
|
+
|
175
|
+
double **dmatrix(long nrl, long nrh, long ncl, long nch)
|
176
|
+
/* allocate a double matrix with subscript range m[nrl..nrh][ncl, nch] */
|
177
|
+
{
|
178
|
+
long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1;
|
179
|
+
double **m;
|
180
|
+
|
181
|
+
/* allocate pointers to rows */
|
182
|
+
m = (double **)malloc((size_t)((nrow + NR_END) * sizeof(double *)));
|
183
|
+
if (!m) nrerror("allocation failure 1 in matrix()");
|
184
|
+
m += NR_END;
|
185
|
+
m -= nrl;
|
186
|
+
|
187
|
+
/* allocate rows and set pointers to them */
|
188
|
+
m[nrl] = (double *)malloc((size_t)((nrow * ncol + NR_END) * sizeof(double)));
|
189
|
+
if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
|
190
|
+
m[nrl] += NR_END;
|
191
|
+
m[nrl] -= ncl;
|
192
|
+
|
193
|
+
for (i = nrl + 1; i <= nrh; i++) m[i] = m[i-1] + ncol;
|
194
|
+
|
195
|
+
/* return pointer to array of pointers to rows */
|
196
|
+
return m;
|
197
|
+
}
|
198
|
+
|
199
|
+
int **imatrix(long nrl, long nrh, long ncl, long nch)
|
200
|
+
/* allocate an int matrix with subscript range m[nrl..nrh][ncl, nch] */
|
201
|
+
{
|
202
|
+
long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1;
|
203
|
+
int **m;
|
204
|
+
|
205
|
+
/* allocate pointers to rows */
|
206
|
+
m = (int **)malloc((size_t)((nrow + NR_END) * sizeof(int *)));
|
207
|
+
if (!m) nrerror("allocation failure 1 in matrix()");
|
208
|
+
m += NR_END;
|
209
|
+
m -= nrl;
|
210
|
+
|
211
|
+
/* allocate rows and set pointers to them */
|
212
|
+
m[nrl] = (int *)malloc((size_t)((nrow * ncol + NR_END) * sizeof(int)));
|
213
|
+
if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
|
214
|
+
m[nrl] += NR_END;
|
215
|
+
m[nrl] -= ncl;
|
216
|
+
|
217
|
+
for (i = nrl + 1; i <= nrh; i++) m[i] = m[i-1] + ncol;
|
218
|
+
|
219
|
+
/* return pointer to array of pointers to rows */
|
220
|
+
return m;
|
221
|
+
}
|
222
|
+
|
223
|
+
float **submatrix(float **a, long oldrl, long oldrh, long oldcl, long oldch,
|
224
|
+
long newrl, long newcl)
|
225
|
+
/* point a submatrix [newrl..][newcl..]
|
226
|
+
to a[oldrl..oldrh][oldcl..oldch] */
|
227
|
+
|
228
|
+
{
|
229
|
+
long i, j, nrow = oldrh - oldrl + 1, ncol = oldcl - newcl;
|
230
|
+
float **m;
|
231
|
+
|
232
|
+
/* allocate array of pointers to rows */
|
233
|
+
m = (float **)malloc((size_t)((nrow + NR_END) * sizeof(float *)));
|
234
|
+
if (!m) nrerror("allocation failure in submatrix()");
|
235
|
+
m += NR_END;
|
236
|
+
m -= newrl;
|
237
|
+
|
238
|
+
/* set pointers to rows */
|
239
|
+
for (i = oldrl, j = newrl; i <= oldrh; i++, j++) m[j] = a[i] + ncol;
|
240
|
+
|
241
|
+
/* return pointer to array of pointers to rows */
|
242
|
+
return m;
|
243
|
+
}
|
244
|
+
|
245
|
+
float **convert_matrix(float *a, long nrl, long nrh, long ncl, long nch)
|
246
|
+
/* allocate a float matrix m[nrl..nrh][ncl..nch] that points to the matrix
|
247
|
+
declared in the standard C manner as a[nrow][ncol],
|
248
|
+
where nrow = nrh - nrl + 1 and ncol = nch - ncl + 1. The routine should
|
249
|
+
be called with the address &a[0][0] as the first argument. */
|
250
|
+
{
|
251
|
+
long i, j, nrow = nrh -nrl + 1, ncol = nch - ncl + 1;
|
252
|
+
float **m;
|
253
|
+
|
254
|
+
/* allocate pointers to rows */
|
255
|
+
m = (float **)malloc((size_t)((nrow + NR_END) * sizeof(float *)));
|
256
|
+
if (!m) nrerror("allocation failure in convert_matrix()");
|
257
|
+
m += NR_END;
|
258
|
+
m -= nrl;
|
259
|
+
|
260
|
+
/* set pointers to rows */
|
261
|
+
m[nrl] = a - ncl;
|
262
|
+
for (i = 1, j = nrl + 1; i < nrow; i++, j++) m[j] = m[j - 1] + ncol;
|
263
|
+
|
264
|
+
/* return pointer to array of pointers to rows */
|
265
|
+
return m;
|
266
|
+
}
|
267
|
+
|
268
|
+
float ***f3tensor(long nrl, long nrh, long ncl, long nch, long ndl, long ndh)
|
269
|
+
/* allocate a float 3tensor with range t[nrl..nrh][ncl..nch][ndl..ndh] */
|
270
|
+
{
|
271
|
+
long i, j, nrow = nrh - nrl + 1, ncol = nch - ncl + 1, ndep = ndh - ndl + 1;
|
272
|
+
float ***t;
|
273
|
+
|
274
|
+
/* allocate pointers to pointers to rows */
|
275
|
+
t = (float ***)malloc((size_t)((nrow + NR_END) * sizeof(float **)));
|
276
|
+
if (!t) nrerror("allocation failure 1 in f3tensor()");
|
277
|
+
t += NR_END;
|
278
|
+
t -= nrl;
|
279
|
+
|
280
|
+
/* allocate pointers to rows and set pointers to them */
|
281
|
+
t[nrl] = (float **)malloc((size_t)((nrow * ncol + NR_END)
|
282
|
+
* sizeof(float *)));
|
283
|
+
if (!t[nrl]) nrerror("allocation failure 2 in f3tensor()");
|
284
|
+
t[nrl] += NR_END;
|
285
|
+
t[nrl] -= ncl;
|
286
|
+
|
287
|
+
/* allocate rows and set pointers to them */
|
288
|
+
t[nrl][ncl] = (float *)malloc((size_t)((nrow * ncol * ndep + NR_END)
|
289
|
+
* sizeof(float)));
|
290
|
+
if (!t[nrl][ncl]) nrerror("allocation failure 3 in f3tensor()");
|
291
|
+
t[nrl][ncl] += NR_END;
|
292
|
+
t[nrl][ncl] -= ndl;
|
293
|
+
|
294
|
+
for (j = ncl + 1; j <= nch; j++) t[nrl][j] = t[nrl][j - 1] + ndep;
|
295
|
+
for (i = nrl + 1; i <= nrh; i++) {
|
296
|
+
t[i] = t[i - 1] + ncol;
|
297
|
+
t[i][ncl] = t[i - 1][ncl] + ncol * ndep;
|
298
|
+
for ( j = ncl + 1; j <= nch; j++) t[i][j] = t[i][j - 1] + ndep;
|
299
|
+
}
|
300
|
+
|
301
|
+
/* return pointer to array of pointers to rows */
|
302
|
+
return t;
|
303
|
+
}
|
304
|
+
|
305
|
+
void free_vector(float *v, long nl, long nh)
|
306
|
+
/* free a float vector allocated with vector() */
|
307
|
+
{
|
308
|
+
free((FREE_ARG)(v + nl - NR_END));
|
309
|
+
}
|
310
|
+
|
311
|
+
void free_ivector(int *v, long nl, long nh)
|
312
|
+
/* free an int vector allocated with ivector() */
|
313
|
+
{
|
314
|
+
free((FREE_ARG)(v + nl - NR_END));
|
315
|
+
}
|
316
|
+
|
317
|
+
void free_cvector(unsigned char *v, long nl, long nh)
|
318
|
+
/* free an unsigned char vector allocated with cvector() */
|
319
|
+
{
|
320
|
+
free((FREE_ARG)(v + nl - NR_END));
|
321
|
+
}
|
322
|
+
|
323
|
+
void free_lvector(unsigned long *v, long nl, long nh)
|
324
|
+
/* free an unsigned long vector allocated with lvector() */
|
325
|
+
{
|
326
|
+
free((FREE_ARG)(v + nl - NR_END));
|
327
|
+
}
|
328
|
+
|
329
|
+
void free_dvector(double *v, long nl, long nh)
|
330
|
+
/* free a double vector allocated with dvector() */
|
331
|
+
{
|
332
|
+
free((FREE_ARG)(v + nl - NR_END));
|
333
|
+
}
|
334
|
+
|
335
|
+
void free_matrix(float **m, long nrl, long nrh, long ncl, long nch)
|
336
|
+
/* free a float matrix allocated by matrix() */
|
337
|
+
{
|
338
|
+
free((FREE_ARG)(m[nrl] + ncl - NR_END));
|
339
|
+
free((FREE_ARG)(m + nrl - NR_END));
|
340
|
+
}
|
341
|
+
|
342
|
+
void free_dmatrix(double **m, long nrl, long nrh, long ncl, long nch)
|
343
|
+
/* free a double matrix allocated by dmatrix() */
|
344
|
+
{
|
345
|
+
free((FREE_ARG)(m[nrl] + ncl - NR_END));
|
346
|
+
free((FREE_ARG)(m + nrl - NR_END));
|
347
|
+
}
|
348
|
+
|
349
|
+
void free_imatrix(int **m, long nrl, long nrh, long ncl, long nch)
|
350
|
+
/* free an int matrix allocated by imatrix() */
|
351
|
+
{
|
352
|
+
free((FREE_ARG)(m[nrl] + ncl - NR_END));
|
353
|
+
free((FREE_ARG)(m + nrl - NR_END));
|
354
|
+
}
|
355
|
+
|
356
|
+
void free_submatirx(float **b, long nrl, long nrh, long ncl, long nch)
|
357
|
+
/* free a submatirx allocated by submatirx() */
|
358
|
+
{
|
359
|
+
free((FREE_ARG)(b + nrl - NR_END));
|
360
|
+
}
|
361
|
+
|
362
|
+
void free_convert_matirx(float **b, long nrl, long nrh, long ncl, long nch)
|
363
|
+
/* free a matirx allocated by convert_matirx() */
|
364
|
+
{
|
365
|
+
free((FREE_ARG)(b + nrl - NR_END));
|
366
|
+
}
|
367
|
+
|
368
|
+
void free_f3tensor(float ***t, long nrl, long nrh, long ncl, long nch,
|
369
|
+
long ndl, long ndh)
|
370
|
+
/* free a float f3tensor allocated by f3tensor() */
|
371
|
+
{
|
372
|
+
free((FREE_ARG)(t[nrl][ncl] + ndl - NR_END));
|
373
|
+
free((FREE_ARG)(t[nrl] + ncl - NR_END));
|
374
|
+
free((FREE_ARG)(t + nrl - NR_END));
|
375
|
+
}
|
376
|
+
|
data/ext/svd.c
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <ruby/ruby.h>
|
3
|
+
#include "svd.h"
|
4
|
+
|
5
|
+
VALUE decompose(VALUE module, VALUE matrix_ruby, VALUE m_ruby, VALUE n_ruby) {
|
6
|
+
int m = NUM2INT(m_ruby);
|
7
|
+
int n = NUM2INT(n_ruby);
|
8
|
+
float **u = matrix(1, m, 1, n);
|
9
|
+
float **v = matrix(1, m, 1, n);
|
10
|
+
float *w = vector(1, n);
|
11
|
+
VALUE *matrix_values = RARRAY_PTR(matrix_ruby);
|
12
|
+
int offset = 0;
|
13
|
+
int i, j;
|
14
|
+
|
15
|
+
/* output arrays */
|
16
|
+
VALUE u_output = rb_ary_new();
|
17
|
+
VALUE v_output = rb_ary_new();
|
18
|
+
VALUE w_output = rb_ary_new();
|
19
|
+
VALUE output = rb_ary_new();
|
20
|
+
|
21
|
+
// precondition
|
22
|
+
if((m*n) != RARRAY_LEN(matrix_ruby)) {
|
23
|
+
rb_raise(rb_eRangeError, "Size of the array is not equal to m * n");
|
24
|
+
return;
|
25
|
+
}
|
26
|
+
|
27
|
+
// convert to u matrix
|
28
|
+
for(i = 1; i <= m; i++) {
|
29
|
+
for(j = 1; j <= n; j++) {
|
30
|
+
offset = ((i-1)*n) + (j-1);
|
31
|
+
u[i][j] = (float) NUM2DBL(matrix_values[offset]);
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
/* perform SVD */
|
36
|
+
svdcmp(u, m, n, w, v);
|
37
|
+
|
38
|
+
/* create w output array */
|
39
|
+
for(i = 1; i <= n; i++)
|
40
|
+
rb_ary_push(w_output, rb_float_new(w[i]));
|
41
|
+
|
42
|
+
/* create u arrays */
|
43
|
+
for(i = 1; i <= m; i++) {
|
44
|
+
for(j = 1; j <= n; j++) {
|
45
|
+
rb_ary_push(u_output, rb_float_new(u[i][j]));
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
/* create v arrays */
|
50
|
+
for(i = 1; i <= n; i++) {
|
51
|
+
for(j = 1; j <= n; j++) {
|
52
|
+
rb_ary_push(v_output, rb_float_new(v[i][j]));
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
rb_ary_push(output, u_output);
|
57
|
+
rb_ary_push(output, w_output);
|
58
|
+
rb_ary_push(output, v_output);
|
59
|
+
return output;
|
60
|
+
}
|
61
|
+
|
62
|
+
void Init_svd()
|
63
|
+
{
|
64
|
+
VALUE module = rb_define_module("SVD");
|
65
|
+
rb_define_module_function(module, "decompose", decompose, 3);
|
66
|
+
}
|
data/ext/svd.h
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
/* svd.h */
|
2
|
+
/* Singular Value Decomposition for solving linear algebraic equations */
|
3
|
+
#include <stdio.h>
|
4
|
+
#include <stdlib.h>
|
5
|
+
#include <math.h>
|
6
|
+
#include "nrutil.h"
|
7
|
+
|
8
|
+
float pythag(float a, float b)
|
9
|
+
{
|
10
|
+
float absa,absb;
|
11
|
+
absa=fabs(a);
|
12
|
+
absb=fabs(b);
|
13
|
+
if (absa > absb) return absa*sqrt(1.0+SQR(absb/absa));
|
14
|
+
else return (absb == 0.0 ? 0.0 : absb*sqrt(1.0+SQR(absa/absb)));
|
15
|
+
}
|
16
|
+
|
17
|
+
void svdcmp(float **a, int m, int n, float w[], float **v)
|
18
|
+
{
|
19
|
+
float pythag(float a, float b);
|
20
|
+
int flag,i,its,j,jj,k,l,nm;
|
21
|
+
float anorm,c,f,g,h,s,scale,x,y,z,*rv1;
|
22
|
+
|
23
|
+
rv1=vector(1,n);
|
24
|
+
g=scale=anorm=0.0;
|
25
|
+
for (i=1;i<=n;i++) {
|
26
|
+
l=i+1;
|
27
|
+
rv1[i]=scale*g;
|
28
|
+
g=s=scale=0.0;
|
29
|
+
if (i <= m) {
|
30
|
+
for (k=i;k<=m;k++) scale += fabs(a[k][i]);
|
31
|
+
if (scale) {
|
32
|
+
for (k=i;k<=m;k++) {
|
33
|
+
a[k][i] /= scale;
|
34
|
+
s += a[k][i]*a[k][i];
|
35
|
+
}
|
36
|
+
f=a[i][i];
|
37
|
+
g = -SIGN(sqrt(s),f);
|
38
|
+
h=f*g-s;
|
39
|
+
a[i][i]=f-g;
|
40
|
+
for (j=l;j<=n;j++) {
|
41
|
+
for (s=0.0,k=i;k<=m;k++) s += a[k][i]*a[k][j];
|
42
|
+
f=s/h;
|
43
|
+
for (k=i;k<=m;k++) a[k][j] += f*a[k][i];
|
44
|
+
}
|
45
|
+
for (k=i;k<=m;k++) a[k][i] *= scale;
|
46
|
+
}
|
47
|
+
}
|
48
|
+
w[i]=scale *g;
|
49
|
+
g=s=scale=0.0;
|
50
|
+
if (i <= m && i != n) {
|
51
|
+
for (k=l;k<=n;k++) scale += fabs(a[i][k]);
|
52
|
+
if (scale) {
|
53
|
+
for (k=l;k<=n;k++) {
|
54
|
+
a[i][k] /= scale;
|
55
|
+
s += a[i][k]*a[i][k];
|
56
|
+
}
|
57
|
+
f=a[i][l];
|
58
|
+
g = -SIGN(sqrt(s),f);
|
59
|
+
h=f*g-s;
|
60
|
+
a[i][l]=f-g;
|
61
|
+
for (k=l;k<=n;k++) rv1[k]=a[i][k]/h;
|
62
|
+
for (j=l;j<=m;j++) {
|
63
|
+
for (s=0.0,k=l;k<=n;k++) s += a[j][k]*a[i][k];
|
64
|
+
for (k=l;k<=n;k++) a[j][k] += s*rv1[k];
|
65
|
+
}
|
66
|
+
for (k=l;k<=n;k++) a[i][k] *= scale;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
anorm=FMAX(anorm,(fabs(w[i])+fabs(rv1[i])));
|
70
|
+
}
|
71
|
+
for (i=n;i>=1;i--) {
|
72
|
+
if (i < n) {
|
73
|
+
if (g) {
|
74
|
+
for (j=l;j<=n;j++)
|
75
|
+
v[j][i]=(a[i][j]/a[i][l])/g;
|
76
|
+
for (j=l;j<=n;j++) {
|
77
|
+
for (s=0.0,k=l;k<=n;k++) s += a[i][k]*v[k][j];
|
78
|
+
for (k=l;k<=n;k++) v[k][j] += s*v[k][i];
|
79
|
+
}
|
80
|
+
}
|
81
|
+
for (j=l;j<=n;j++) v[i][j]=v[j][i]=0.0;
|
82
|
+
}
|
83
|
+
v[i][i]=1.0;
|
84
|
+
g=rv1[i];
|
85
|
+
l=i;
|
86
|
+
}
|
87
|
+
for (i=IMIN(m,n);i>=1;i--) {
|
88
|
+
l=i+1;
|
89
|
+
g=w[i];
|
90
|
+
for (j=l;j<=n;j++) a[i][j]=0.0;
|
91
|
+
if (g) {
|
92
|
+
g=1.0/g;
|
93
|
+
for (j=l;j<=n;j++) {
|
94
|
+
for (s=0.0,k=l;k<=m;k++) s += a[k][i]*a[k][j];
|
95
|
+
f=(s/a[i][i])*g;
|
96
|
+
for (k=i;k<=m;k++) a[k][j] += f*a[k][i];
|
97
|
+
}
|
98
|
+
for (j=i;j<=m;j++) a[j][i] *= g;
|
99
|
+
} else for (j=i;j<=m;j++) a[j][i]=0.0;
|
100
|
+
++a[i][i];
|
101
|
+
}
|
102
|
+
for (k=n;k>=1;k--) {
|
103
|
+
for (its=1;its<=30;its++) {
|
104
|
+
flag=1;
|
105
|
+
for (l=k;l>=1;l--) {
|
106
|
+
nm=l-1;
|
107
|
+
if ((float)(fabs(rv1[l])+anorm) == anorm) {
|
108
|
+
flag=0;
|
109
|
+
break;
|
110
|
+
}
|
111
|
+
if ((float)(fabs(w[nm])+anorm) == anorm) break;
|
112
|
+
}
|
113
|
+
if (flag) {
|
114
|
+
c=0.0;
|
115
|
+
s=1.0;
|
116
|
+
for (i=l;i<=k;i++) {
|
117
|
+
f=s*rv1[i];
|
118
|
+
rv1[i]=c*rv1[i];
|
119
|
+
if ((float)(fabs(f)+anorm) == anorm) break;
|
120
|
+
g=w[i];
|
121
|
+
h=pythag(f,g);
|
122
|
+
w[i]=h;
|
123
|
+
h=1.0/h;
|
124
|
+
c=g*h;
|
125
|
+
s = -f*h;
|
126
|
+
for (j=1;j<=m;j++) {
|
127
|
+
y=a[j][nm];
|
128
|
+
z=a[j][i];
|
129
|
+
a[j][nm]=y*c+z*s;
|
130
|
+
a[j][i]=z*c-y*s;
|
131
|
+
}
|
132
|
+
}
|
133
|
+
}
|
134
|
+
z=w[k];
|
135
|
+
if (l == k) {
|
136
|
+
if (z < 0.0) {
|
137
|
+
w[k] = -z;
|
138
|
+
for (j=1;j<=n;j++) v[j][k] = -v[j][k];
|
139
|
+
}
|
140
|
+
break;
|
141
|
+
}
|
142
|
+
if (its == 30) nrerror("no convergence in 30 svdcmp iterations");
|
143
|
+
x=w[l];
|
144
|
+
nm=k-1;
|
145
|
+
y=w[nm];
|
146
|
+
g=rv1[nm];
|
147
|
+
h=rv1[k];
|
148
|
+
f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
|
149
|
+
g=pythag(f,1.0);
|
150
|
+
f=((x-z)*(x+z)+h*((y/(f+SIGN(g,f)))-h))/x;
|
151
|
+
c=s=1.0;
|
152
|
+
for (j=l;j<=nm;j++) {
|
153
|
+
i=j+1;
|
154
|
+
g=rv1[i];
|
155
|
+
y=w[i];
|
156
|
+
h=s*g;
|
157
|
+
g=c*g;
|
158
|
+
z=pythag(f,h);
|
159
|
+
rv1[j]=z;
|
160
|
+
c=f/z;
|
161
|
+
s=h/z;
|
162
|
+
f=x*c+g*s;
|
163
|
+
g = g*c-x*s;
|
164
|
+
h=y*s;
|
165
|
+
y *= c;
|
166
|
+
for (jj=1;jj<=n;jj++) {
|
167
|
+
x=v[jj][j];
|
168
|
+
z=v[jj][i];
|
169
|
+
v[jj][j]=x*c+z*s;
|
170
|
+
v[jj][i]=z*c-x*s;
|
171
|
+
}
|
172
|
+
z=pythag(f,h);
|
173
|
+
w[j]=z;
|
174
|
+
if (z) {
|
175
|
+
z=1.0/z;
|
176
|
+
c=f*z;
|
177
|
+
s=h*z;
|
178
|
+
}
|
179
|
+
f=c*g+s*y;
|
180
|
+
x=c*y-s*g;
|
181
|
+
for (jj=1;jj<=m;jj++) {
|
182
|
+
y=a[jj][j];
|
183
|
+
z=a[jj][i];
|
184
|
+
a[jj][j]=y*c+z*s;
|
185
|
+
a[jj][i]=z*c-y*s;
|
186
|
+
}
|
187
|
+
}
|
188
|
+
rv1[l]=0.0;
|
189
|
+
rv1[k]=f;
|
190
|
+
w[k]=x;
|
191
|
+
}
|
192
|
+
}
|
193
|
+
free_vector(rv1,1,n);
|
194
|
+
}
|
data/lib/ruby-svd.rb
ADDED
data/lib/svd_matrix.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'mathn'
|
2
|
+
require 'svd'
|
3
|
+
|
4
|
+
class SVDMatrix < Matrix
|
5
|
+
public_class_method :new
|
6
|
+
def initialize(m, n)
|
7
|
+
@rows = Array.new(m)
|
8
|
+
m.times {|i| @rows[i] = Array.new(n)}
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(i, j, val)
|
12
|
+
@rows[i][j] = val
|
13
|
+
end
|
14
|
+
|
15
|
+
def []=(i, row)
|
16
|
+
@rows[i] = row
|
17
|
+
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
@rows.collect {|row| "#{row.inspect}\n"}
|
21
|
+
end
|
22
|
+
|
23
|
+
def lsa
|
24
|
+
input_array = []
|
25
|
+
@rows.each {|row| input_array += row}
|
26
|
+
u_array, w_array, v_array = SVD.decompose(input_array, row_size, column_size)
|
27
|
+
|
28
|
+
u = SVDMatrix.new(row_size, column_size)
|
29
|
+
row_size.times {|i| u[i] = u_array.slice!(0,column_size)}
|
30
|
+
|
31
|
+
v = SVDMatrix.new(column_size, column_size)
|
32
|
+
column_size.times {|i| v[i] = v_array.slice!(0,column_size)}
|
33
|
+
v = v.transpose
|
34
|
+
|
35
|
+
w_array = w_array[0...2] + Array.new(w_array.size - 2).collect {|i| 0}
|
36
|
+
w = Matrix.diagonal(*w_array)
|
37
|
+
|
38
|
+
r = (u * w * v)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-svd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Will
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-03-18 00:00:00 +11:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Singular Value Decomposition with no dependency on GSL or LAPACK
|
22
|
+
email: me@willcannings.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions:
|
26
|
+
- ext/extconf.rb
|
27
|
+
extra_rdoc_files:
|
28
|
+
- LICENSE
|
29
|
+
- README
|
30
|
+
files:
|
31
|
+
- ext/extconf.rb
|
32
|
+
- ext/nrutil.h
|
33
|
+
- ext/svd.c
|
34
|
+
- ext/svd.h
|
35
|
+
- lib/ruby-svd.rb
|
36
|
+
- lib/svd_matrix.rb
|
37
|
+
- LICENSE
|
38
|
+
- README
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/willcannings/ruby-svd
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options:
|
45
|
+
- --charset=UTF-8
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
- ext
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.6
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: SVD for Ruby
|
70
|
+
test_files: []
|
71
|
+
|