BaiduFMX
652 строки · 14.9 Кб
1unit SimpleSVD;
2
3interface
4
5uses
6System.Classes,
7System.SysUtils,
8System.Math;
9
10type
11TSVDFloat = Double;
12TSVDFloatArray = TArray<TSVDFloat>;
13
14TMatrix = class
15private
16FRows: Integer;
17FCols: Integer;
18FName: string;
19FType: Integer;
20FMat: TSVDFloatArray;
21function GetMat(Index: Integer): TSVDFloat;
22procedure SetMat(Index: Integer; const Value: TSVDFloat);
23function GetMatrix(row, col: Integer): TSVDFloat;
24procedure SetMatrix(row, col: Integer; const Value: TSVDFloat);
25public const
26AS_MATRIX = 1;
27AS_VECTOR = 2;
28public
29constructor Create(M, N: Integer; aName: string; aType: Integer);
30procedure Output(Lines: TStrings);
31procedure SetAll(const aMat: TSVDFloatArray);
32property Mat[Index: Integer]: TSVDFloat read GetMat write SetMat;
33property Rows: Integer read FRows;
34property Cols: Integer read FCols;
35property Name: string read FName;
36property MatrixType: Integer read FType;
37property Matrix[row, col: Integer]: TSVDFloat read GetMatrix write SetMatrix; default;
38end;
39
40TSVD = class
41public
42constructor Create;
43function Multiply (m1, m2, y: TMatrix): Integer;
44function PinvCompute(A: TMatrix; rows, cols: Integer): TMatrix;
45function PseudoInverseSVD(const V,S,U: TSVDFloatArray; rows,cols: Integer): TSVDFloatArray;
46procedure GivensL(var S_: TSVDFloatArray; const dim: TArray<Integer>; m: Integer; a, b: TSVDFloat);
47procedure GivensR(var S_: TSVDFloatArray; const dim: TArray<Integer>; m: Integer; a, b: TSVDFloat);
48procedure SVDDecomp(const dim: TArray<Integer>; var U_, S_, V_: TSVDFloatArray; eps: Double);
49procedure SVDCompute(M, N: Integer; const A: TSVDFloatArray; LDA: Integer;
50var S, U: TSVDFloatArray; LDU: Integer; var VT: TSVDFloatArray;
51LDVT: Integer);
52end;
53implementation
54
55{ TMatrix }
56
57constructor TMatrix.Create(M, N: Integer; aName: string; aType: Integer);
58begin
59FRows := M;
60FCols := N;
61FName := aName;
62FType := aType;
63SetLength(FMat, M * N);
64end;
65
66function TMatrix.GetMat(Index: Integer): TSVDFloat;
67begin
68Assert(Index < FRows * FCols);
69Result := FMat[Index];
70end;
71
72function TMatrix.GetMatrix(row, col: Integer): TSVDFloat;
73begin
74Assert((row < FRows) and (col < FCols));
75Result := FMat[row * FCols + col];
76end;
77
78procedure TMatrix.Output(Lines: TStrings);
79var
80s: string;
81i, j: Integer;
82begin
83if(FType=AS_MATRIX) then
84begin
85Lines.Add('Matrix ' + FName + ':');
86for i:=0 to FRows-1 do
87begin
88s := '';
89for j:=0 to FCols-1 do
90begin
91if(FMat[i*cols+j]<10E-13) and (FMat[i*cols+j]>-10E-13) then
92mat[i*cols+j] := 0;
93s := s + Format('%12.4g', [FMat[i*FCols+j]]);
94end;
95Lines.Add(s);
96end;
97Lines.Add('');
98end
99else if(FType=AS_VECTOR) then
100begin
101Lines.Add('Vector ' + FName + ':');
102s := '';
103for i:=0 to FRows-1 do
104begin
105if(FMat[i]<10E-13) and (FMat[i]>-10E-13) then
106mat[i*cols] := 0;
107s := s + Format('%13.4f', [FMat[i]]);
108end;
109Lines.Add(s);
110Lines.Add('');
111end;
112end;
113
114procedure TMatrix.SetAll(const aMat: TSVDFloatArray);
115begin
116FMat := aMat;
117end;
118
119procedure TMatrix.SetMat(Index: Integer; const Value: TSVDFloat);
120begin
121Assert(Index < FRows * FCols);
122FMat[Index] := Value;
123end;
124
125procedure TMatrix.SetMatrix(row, col: Integer; const Value: TSVDFloat);
126begin
127Assert((row < FRows) and (col < FCols));
128FMat[row * FCols + col] := Value;
129end;
130
131{ TSVD }
132
133constructor TSVD.Create;
134begin
135
136end;
137
138//#define U(i,j) U_[(i)*dim[0]+(j)]
139//#define S(i,j) S_[(i)*dim[1]+(j)]
140//#define V(i,j) V_[(i)*dim[1]+(j)]
141
142procedure TSVD.GivensL(var S_: TSVDFloatArray; const dim: TArray<Integer>;
143m: Integer; a, b: TSVDFloat);
144var
145r, c, s: TSVDFloat;
146i, idx0, idx1: Integer;
147S0, S1: TSVDFloat;
148begin
149r:=sqrt(a*a+b*b);
150c:=a/r;
151s:=-b/r;
152
153for i:=0 to dim[1]-1 do
154begin
155idx0 := (m+0)*dim[0]+(i); //S(m+0,i)
156idx1 := (m+1)*dim[0]+(i); //S(m+1,i)
157S0:=S_[idx0];
158S1:=S_[idx1];
159S_[idx0] := S_[idx0] + S0*(c-1) + S1*(-s);
160S_[idx1] := S_[idx1] + S0*(s) + S1*(c-1);
161end;
162end;
163
164procedure TSVD.GivensR(var S_: TSVDFloatArray; const dim: TArray<Integer>;
165m: Integer; a, b: TSVDFloat);
166var
167r, c, s: TSVDFloat;
168i, idx0, idx1: Integer;
169S0, S1: TSVDFloat;
170begin
171r:=sqrt(a*a+b*b);
172c:=a/r;
173s:=-b/r;
174for i:=0 to dim[0]-1 do
175begin
176idx0 := (i)*dim[0]+(m+0); //S(i,m+0)
177idx1 := (i)*dim[0]+(m+1); //S(i,m+1);
178
179S0:=S_[idx0];
180S1:=S_[idx1];
181S_[idx0] := S_[idx0] + S0*(c-1) + S1*(-s);
182S_[idx1] := S_[idx1] + S0*(s) + S1*(c-1);
183end;
184end;
185
186function TSVD.Multiply(m1, m2, y: TMatrix): Integer;
187var
188sum: Double;
189I, J, K: Integer;
190begin
191if (m1.Cols <> m2.Rows) then
192Exit(0);
193
194for I := 0 to m1.Rows-1 do
195begin
196for J := 0 to m2.Cols-1 do
197begin
198sum := 0;
199for K := 0 to m1.Cols-1 do
200begin
201sum := sum + m1[i,k] * m2[k,j];
202end;
203y[i,j] := sum;
204end;
205end;
206
207Result := 1;
208end;
209
210function TSVD.PinvCompute(A: TMatrix; rows, cols: Integer): TMatrix;
211var
212U, VT, S: TMatrix;
213Mat: TSVDFloatArray;
214I, J: Integer;
215m, n: Integer;
216tU, tS, tVT, pinv: TSVDFloatArray;
217begin
218Result := TMatrix.Create(cols,rows,'A_pinv',TMatrix.AS_MATRIX);
219U:=TMatrix.Create(rows,cols,'U',TMatrix.AS_MATRIX);
220VT:=TMatrix.Create(cols,cols,'VT',TMatrix.AS_MATRIX);
221S:=TMatrix.Create(cols,cols,'S',TMatrix.AS_MATRIX);
222SetLength(Mat, rows*cols);
223try
224for I:=0 to rows*cols-1 do
225Mat[i]:=A.Mat[i];
226
227m := cols;
228n := rows;
229SetLength(tU, m*m);
230SetLength(tS, m);
231SetLength(tVT, m*n);
232
233SvdCompute(m, n, Mat, m, tS, tU, m, tVT, m);
234
235for I:=0 to rows-1 do
236for j:=0 to cols-1 do
237U.Mat[i*cols+j] := tVT[i*cols+j];
238
239for i:=0 to cols-1 do
240for j:=0 to cols-1 do
241VT.Mat[i*cols+j] := tU[i*cols+j];
242
243for i:=0 to cols-1 do
244for j:=0 to cols-1 do
245begin
246if(i=j) then
247S.Mat[i*cols+j]:=tS[i]
248else
249S.Mat[i*cols+j]:=0;
250end;
251pinv := PseudoInverseSVD(tVT,tS,tU,m,n);
252Result.SetAll(pinv);
253finally
254S.Free;
255VT.Free;
256U.Free;
257end;
258end;
259
260function TSVD.PseudoInverseSVD(const V, S, U: TSVDFloatArray; rows,
261cols: Integer): TSVDFloatArray;
262var
263temp: TSVDFloatArray;
264I, J, K: Integer;
265sum: TSVDFloat;
266begin
267SetLength(temp, rows*rows);
268
269for I:=0 to rows-1 do
270for J := 0 to rows-1 do
271temp[i*rows+j] := U[j*rows+i]/S[j];
272SetLength(Result, rows*cols);
273
274for i:=0 to rows-1 do
275for j:=0 to cols-1 do
276begin
277sum:=0;
278for k:=0 to rows-1 do
279sum:=sum+temp[i*rows+k]*V[j*rows+k];
280Result[i*cols+j]:=sum;
281end;
282end;
283
284procedure TSVD.SVDCompute(M, N: Integer; const A: TSVDFloatArray; LDA: Integer;
285var S, U: TSVDFloatArray; LDU: Integer; var VT: TSVDFloatArray;
286LDVT: Integer);
287var
288dim: TArray<Integer>;
289U_, V_, S_: TSVDFloatArray;
290i, j: Integer;
291tmp: TSVDFloat;
292begin
293dim := TArray<Integer>.Create(Max(N, M), Min(N, M));
294SetLength(U_, dim[0]*dim[0]);
295SetLength(V_, dim[1]*dim[1]);
296SetLength(S_, dim[0]*dim[1]);
297FillChar(U_[0], dim[0]*dim[0]*SizeOf(TSVDFloat), 0);
298FillChar(V_[0], dim[1]*dim[1]*SizeOf(TSVDFloat), 0);
299
300if(dim[1]=M) then
301begin
302for I := 0 to dim[0]-1 do
303for J := 0 to dim[1]-1 do
304begin
305S_[i*dim[1]+j]:=A[i*lda+j];
306end;
307end
308else
309begin
310for I := 0 to dim[0]-1 do
311for J := 0 to dim[1]-1 do
312begin
313S_[i*dim[1]+j]:=A[j*lda+i];
314end;
315end;
316for i:=0 to dim[0]-1 do
317begin
318U_[i*dim[0]+i]:=1;
319end;
320for i:=0 to dim[1]-1 do
321begin
322V_[i*dim[1]+i]:=1;
323end;
324
325SvdDecomp(dim, U_, S_, V_, -1.0);
326
327for i:=0 to dim[1]-1 do
328begin
329S[i]:=S_[i*dim[1]+i];
330end;
331if(dim[1]=M) then
332begin
333for I := 0 to dim[1]-1 do
334for J := 0 to M-1 do
335begin
336if S[i] < 0.0 then
337tmp := -1.0
338else
339tmp := 1.0;
340U[j+ldu*i]:=V_[j+i*dim[1]]*tmp;
341end;
342end
343else
344begin
345for I := 0 to dim[1]-1 do
346for J := 0 to M-1 do
347begin
348if S[i] < 0.0 then
349tmp := -1.0
350else
351tmp := 1.0;
352U[j+ldu*i]:=U_[i+j*dim[0]]*tmp;
353end;
354end;
355if(dim[0]=N) then
356begin
357for i:=0 to N-1 do
358for j:=0 to dim[1]-1 do
359begin
360VT[j+ldvt*i]:=U_[j+i*dim[0]];
361end;
362end
363else
364begin
365for i:=0 to N-1 do
366for j:=0 to dim[1]-1 do
367begin
368VT[j+ldvt*i]:=V_[i+j*dim[1]];
369end;
370end;
371for i:=0 to dim[1]-1 do
372begin
373if S[i] < 0.0 then
374tmp := -1.0
375else
376tmp := 1.0;
377S[i]:=S[i]*tmp;
378end;
379end;
380
381procedure TSVD.SVDDecomp(const dim: TArray<Integer>; var U_, S_,
382V_: TSVDFloatArray; eps: Double);
383var
384N: Integer;
385x1: TSVDFloat;
386x_inv_norm, dot_prod: Double;
387house_vec: TArray<Double>;
388i, j, K, idx, k0, idx2, i0, i1: Integer;
389tmp: TSVDFloat;
390alpha, beta, S_max: Double;
391c: array [0 .. 1, 0 .. 1] of Double;
392b, c2, d, lambda1, lambda2, d1, d2, mu: Double;
393dimU, dimV: TArray<Integer>;
394begin
395Assert(dim[0] >= dim[1]);
396N := Min(dim[0], dim[1]);
397
398SetLength(house_vec, Max(dim[0], dim[1]));
399
400// S(i,j) S_[(i)*dim[1]+(j)]
401for i := 0 to N - 1 do
402begin
403x1 := S_[(i) * dim[1] + (i)]; // S(i,i);
404if (x1 < 0) then
405x1 := -x1;
406
407x_inv_norm := 0;
408
409for j := i to dim[0] - 1 do
410begin
411x_inv_norm := x_inv_norm + Sqr(S_[(j) * dim[1] + (i)]); // s(j,i)*s(j,i)
412end;
413if (x_inv_norm > 0) then
414x_inv_norm := 1 / sqrt(x_inv_norm);
415
416alpha := sqrt(1 + x1 * x_inv_norm);
417beta := x_inv_norm / alpha;
418
419house_vec[i] := -alpha;
420for j := i + 1 to dim[0] - 1 do
421begin
422house_vec[j] := -beta * S_[(j) * dim[1] + (i)];
423end;
424
425if (S_[(i) * dim[1] + (i)] < 0) then
426for j := i + 1 to dim[0] - 1 do
427begin
428house_vec[j] := -house_vec[j];
429end;
430
431for K := i to dim[1] - 1 do
432begin
433dot_prod := 0;
434for j := i to dim[0] - 1 do
435begin
436dot_prod := dot_prod + S_[(j) * dim[1] + (K)] * house_vec[j];
437end;
438for j := i to dim[0] - 1 do
439begin
440idx := (j) * dim[1] + (K);
441S_[idx] := S_[idx] - dot_prod * house_vec[j];
442end;
443end;
444
445// #define U(i,j) U_[(i)*dim[0]+(j)]
446for K := 0 to dim[0] - 1 do
447begin
448dot_prod := 0;
449for j := i to dim[0] - 1 do
450begin
451dot_prod := dot_prod + U_[(K) * dim[0] + (j)] * house_vec[j];
452end;
453for j := i to dim[0] - 1 do
454begin
455idx := (K) * dim[0] + (j);
456U_[idx] := U_[idx] - dot_prod * house_vec[j];
457end;
458end;
459
460if (i >= N - 1) then
461continue;
462begin
463x1 := S_[(i) * dim[1] + (i + 1)]; // S(i,i+1);
464if (x1 < 0) then
465x1 := -x1;
466
467x_inv_norm := 0;
468for j := i + 1 to dim[1] - 1 do
469begin
470x_inv_norm := x_inv_norm + Sqr(S_[(i) * dim[1] + (j)]); // S(i,j);
471end;
472if (x_inv_norm > 0) then
473x_inv_norm := 1 / sqrt(x_inv_norm);
474
475alpha := sqrt(1 + x1 * x_inv_norm);
476beta := x_inv_norm / alpha;
477
478house_vec[i + 1] := -alpha;
479for j := i + 2 to dim[1] - 1 do
480begin
481house_vec[j] := -beta * S_[(i) * dim[1] + (j)]; // S(i,j);
482end;
483if (S_[(i) * dim[1] + (i + 1)] < 0) then
484for j := i + 2 to dim[1] - 1 do
485begin
486house_vec[j] := -house_vec[j];
487end;
488end;
489
490for K := i to dim[0] - 1 do
491begin
492dot_prod := 0;
493for j := i + 1 to dim[1] - 1 do
494begin
495dot_prod := dot_prod + S_[(K) * dim[1] + (j)] * house_vec[j];
496end;
497for j := i + 1 to dim[1] - 1 do
498begin
499idx := (K) * dim[1] + (j);
500S_[idx] := S_[idx] - dot_prod * house_vec[j];
501end;
502end;
503
504// V(i,j) V_[(i)*dim[1]+(j)]
505for K := 0 to dim[1] - 1 do
506begin
507dot_prod := 0;
508for j := i + 1 to dim[1] - 1 do
509begin
510idx := (j) * dim[1] + (K);
511dot_prod := dot_prod + V_[idx] * house_vec[j];
512end;
513for j := i + 1 to dim[1] - 1 do
514begin
515idx := (j) * dim[1] + (K);
516V_[idx] := V_[idx] - dot_prod * house_vec[j];
517end;
518end;
519end;
520
521k0 := 0;
522if (eps < 0) then
523begin
524eps := 1.0;
525while (eps + 1.0 > 1.0) do
526eps := eps * 0.5;
527eps := eps * 64.0;
528end;
529
530while (k0 < dim[1] - 1) do
531begin
532S_max := 0.0;
533for i := 0 to dim[1] - 1 do
534begin
535idx := (i) * dim[1] + (i);
536S_max := Max(S_max, S_[idx]);
537end;
538
539while (k0 < dim[1] - 1) and (Abs(S_[(k0) * dim[1] + (k0 + 1)]) <= eps * S_max) do
540Inc(k0);
541if (k0 = (dim[1] - 1)) then
542continue;
543
544N := k0 + 2;
545while (N < dim[1]) and (Abs(S_[(N - 1) * dim[1] + (N)]) > eps * S_max) do
546Inc(N);
547
548begin
549idx := (N - 2) * dim[1] + (N - 2);
550c[0][0] := Sqr(S_[idx]);
551if (N - k0 > 2) then
552begin
553idx := (N - 3) * dim[1] + (N - 2);
554c[0][0] := c[0][0] + Sqr(S_[idx]);
555end;
556idx := (N - 2) * dim[1] + (N - 2);
557idx2 := (N - 2) * dim[1] + (N - 1);
558c[0][1] := S_[idx] * S_[idx2];
559c[1][0] := c[0][1];
560
561idx := (N - 1) * dim[1] + (N - 1);
562c[1][1] := Sqr(S_[idx]) + Sqr(S_[idx2]);
563
564b := -(c[0][0] + c[1][1]) / 2;
565c2 := c[0][0] * c[1][1] - c[0][1] * c[1][0];
566d := 0;
567if (b * b - c2 > 0) then
568d := sqrt(b * b - c2)
569else
570begin
571b := (c[0][0] - c[1][1]) / 2;
572c2 := -c[0][1] * c[1][0];
573if (b * b - c2 > 0) then
574d := sqrt(b * b - c2);
575end;
576
577lambda1 := -b + d;
578lambda2 := -b - d;
579
580d1 := lambda1 - c[1][1];
581if d1 < 0 then
582d1 := -d1;
583d2 := lambda2 - c[1][1];
584if d2 < 0 then
585d2 := -d2;
586if d1 < d2 then
587mu := lambda1
588else
589mu := lambda2;
590
591idx := (k0) * dim[1] + (k0);
592idx2 := (k0) * dim[1] + (k0 + 1);
593alpha := Sqr(S_[idx]) - mu;
594beta := S_[idx] * S_[idx2];
595end;
596
597for K := k0 to N - 2 do
598begin
599dimU := TArray<Integer>.Create(dim[0], dim[0]);
600dimV := TArray<Integer>.Create(dim[1], dim[1]);
601GivensR(S_, dim, K, alpha, beta);
602GivensL(V_, dimV, K, alpha, beta);
603
604idx := (K) * dim[1] + (K);
605idx2 := (K + 1) * dim[1] + (K);
606alpha := S_[idx];
607beta := S_[idx2];
608GivensL(S_, dim, K, alpha, beta);
609GivensR(U_, dimU, K, alpha, beta);
610
611idx := (K) * dim[1] + (K + 1);
612alpha := S_[idx];
613idx := (K) * dim[1] + (K + 2);
614beta := S_[idx];
615end;
616
617begin
618for i0 := k0 to N - 2 do
619begin
620for i1 := 0 to dim[1] - 1 do
621begin
622if (i0 > i1) or (i0 + 1 < i1) then
623begin
624idx := (i0) * dim[1] + (i1);
625S_[idx] := 0;
626end;
627end;
628end;
629for i0 := 0 to dim[0] - 1 do
630begin
631for i1 := k0 to N - 2 do
632begin
633if (i0 > i1) or (i0 + 1 < i1) then
634begin
635idx := (i0) * dim[1] + (i1);
636S_[idx] := 0;
637end;
638end;
639end;
640for i := 0 to dim[1] - 2 do
641begin
642idx := (i) * dim[1] + (i + 1);
643if (Abs(S_[idx]) <= eps * S_max) then
644begin
645S_[idx] := 0;
646end;
647end;
648end;
649end;
650end;
651
652end.
653