-
-
Notifications
You must be signed in to change notification settings - Fork 29
Expand file tree
/
Copy pathSparseLDL.cs
More file actions
348 lines (293 loc) · 11.7 KB
/
SparseLDL.cs
File metadata and controls
348 lines (293 loc) · 11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
// Copyright (c) 2005-2013, Timothy A. Davis
namespace CSparse.Complex.Factorization
{
using CSparse.Factorization;
using CSparse.Ordering;
using CSparse.Properties;
using CSparse.Storage;
using System;
using System.Numerics;
/// <summary>
/// Sparse LDL' factorization.
/// </summary>
/// <remarks>
/// If A is positive definite then the factorization will be accurate. A can be
/// indefinite (with negative values on the diagonal D), but in this case no
/// guarantee of accuracy is provided, since no numeric pivoting is performed.
///
/// Only the diagonal and upper triangular part of A (or PAP' if a permutation
/// P is provided) is accessed. The lower triangular parts of the matrix A or
/// PAP' can be present, but they are ignored.
/// </remarks>
public class SparseLDL : ISparseFactorization<Complex>
{
SymbolicFactorization S;
CompressedColumnStorage<Complex> L;
double[] D;
readonly int n;
readonly Complex[] temp; // solve workspace
#region Static methods
/// <summary>
/// Creates a sparse LDL' factorization.
/// </summary>
/// <param name="A">Column-compressed, symmetric matrix (may be indefinite).</param>
/// <param name="order">Ordering method to use (natural or A+A').</param>
public static SparseLDL Create(CompressedColumnStorage<Complex> A, ColumnOrdering order)
{
return Create(A, order, null);
}
/// <summary>
/// Creates a sparse LDL' factorization.
/// </summary>
/// <param name="A">Column-compressed, symmetric matrix (may be indefinite).</param>
/// <param name="order">Ordering method to use (natural or A+A').</param>
/// <param name="progress">Report progress (range from 0.0 to 1.0).</param>
public static SparseLDL Create(CompressedColumnStorage<Complex> A, ColumnOrdering order,
IProgress<double> progress)
{
if ((int)order > 1)
{
throw new ArgumentException(Resources.InvalidColumnOrdering, "order");
}
return Create(A, AMD.Generate(A, order), progress);
}
/// <summary>
/// Creates a sparse LDL' factorization.
/// </summary>
/// <param name="A">Column-compressed, symmetric matrix (may be indefinite).</param>
/// <param name="p">Permutation.</param>
public static SparseLDL Create(CompressedColumnStorage<Complex> A, int[] p)
{
return Create(A, p, null);
}
/// <summary>
/// Creates a sparse LDL' factorization.
/// </summary>
/// <param name="A">Column-compressed, symmetric matrix (may be indefinite).</param>
/// <param name="p">Permutation.</param>
/// <param name="progress">Report progress (range from 0.0 to 1.0).</param>
public static SparseLDL Create(CompressedColumnStorage<Complex> A, int[] p,
IProgress<double> progress)
{
Check.NotNull(A, "A");
Check.NotNull(p, "p");
int n = A.ColumnCount;
Check.SquareMatrix(A, "A");
Check.Permutation(p, n, "p");
var C = new SparseLDL(n);
// Ordering and symbolic analysis
C.SymbolicAnalysis(A, p);
// Numeric LDL' factorization
C.Factorize(A, progress);
return C;
}
#endregion
private SparseLDL(int n)
{
this.n = n;
this.temp = new Complex[n];
}
/// <summary>
/// Gets the number of nonzeros of the L.
/// </summary>
public int NonZerosCount
{
get { return L.NonZerosCount; }
}
/// <summary>
/// Solves a linear system Ax=b, where A is symmetric positive definite.
/// </summary>
/// <param name="input">Right hand side b.</param>
/// <param name="result">Solution vector x.</param>
public void Solve(Complex[] input, Complex[] result) => Solve(input.AsSpan(), result.AsSpan());
/// <summary>
/// Solves a linear system Ax=b, where A is symmetric positive definite.
/// </summary>
/// <param name="input">Right hand side b.</param>
/// <param name="result">Solution vector x.</param>
public void Solve(ReadOnlySpan<Complex> input, Span<Complex> result)
{
if (input.IsEmpty) throw new ArgumentNullException(nameof(input));
if (result.IsEmpty) throw new ArgumentNullException(nameof(result));
var x = temp;
Permutation.ApplyInverse(S.pinv, input, x, n); // x = P*b
var lx = L.Values;
var lp = L.ColumnPointers;
var li = L.RowIndices;
var d = this.D;
int end;
// Solve lower triangular system by forward elimination, x = L\x.
for (int i = 0; i < n; i++)
{
end = lp[i + 1];
for (int p = lp[i]; p < end; p++)
{
x[li[p]] -= lx[p] * x[i];
}
}
// Solve diagonal system, x = D\x.
for (int i = 0; i < n; i++)
{
x[i] /= d[i];
}
// Solve upper triangular system by backward elimination, x = L'\x.
for (int i = n - 1; i >= 0; i--)
{
end = lp[i + 1];
for (int p = lp[i]; p < end; p++)
{
x[i] -= Complex.Conjugate(lx[p]) * x[li[p]];
}
}
Permutation.Apply(S.pinv, x, result, n); // b = P'*x
}
/// <summary>
/// Ordering and symbolic analysis for a LDL' factorization.
/// </summary>
/// <param name="A">Matrix to factorize.</param>
/// <param name="P">Permutation.</param>
private void SymbolicAnalysis(CompressedColumnStorage<Complex> A, int[] P)
{
int n = A.ColumnCount;
var sym = this.S = new SymbolicFactorization();
var ap = A.ColumnPointers;
var ai = A.RowIndices;
var Pinv = Permutation.Invert(P);
// Output: column pointers and elimination tree.
var lp = new int[n + 1];
var parent = new int[n];
// Workspace
var lnz = new int[n];
var flag = new int[n];
int i, k, p, kk, p2;
for (k = 0; k < n; k++)
{
// L(k,:) pattern: all nodes reachable in etree from nz in A(0:k-1,k)
parent[k] = -1; // parent of k is not yet known
flag[k] = k; // mark node k as visited
lnz[k] = 0; // count of nonzeros in column k of L
kk = (P != null) ? (P[k]) : (k); // kth original, or permuted, column
p2 = ap[kk + 1];
for (p = ap[kk]; p < p2; p++)
{
// A(i,k) is nonzero (original or permuted A)
i = (Pinv != null) ? (Pinv[ai[p]]) : (ai[p]);
if (i < k)
{
// follow path from i to root of etree, stop at flagged node
for (; flag[i] != k; i = parent[i])
{
// find parent of i if not yet determined
if (parent[i] == -1) parent[i] = k;
lnz[i]++; // L(k,i) is nonzero
flag[i] = k; // mark i as visited
}
}
}
}
// construct Lp index array from Lnz column counts
lp[0] = 0;
for (k = 0; k < n; k++)
{
lp[k + 1] = lp[k] + lnz[k];
}
sym.parent = parent;
sym.cp = lp;
sym.q = P;
sym.pinv = Pinv;
}
/// <summary>
/// Compute the numeric LDL' factorization of PAP'.
/// </summary>
void Factorize(CompressedColumnStorage<Complex> A, IProgress<double> progress)
{
int n = A.ColumnCount;
var ap = A.ColumnPointers;
var ai = A.RowIndices;
var ax = A.Values;
int[] parent = S.parent;
int[] P = S.q;
int[] Pinv = S.pinv;
this.D = new double[n];
this.L = CompressedColumnStorage<Complex>.Create(n, n, S.cp[n]);
Array.Copy(S.cp, L.ColumnPointers, n + 1);
var lp = L.ColumnPointers;
var li = L.RowIndices;
var lx = L.Values;
// Workspace
var y = new Complex[n];
var pattern = new int[n];
var flag = new int[n];
var lnz = new int[n];
Complex yi, l_ki, d;
int i, k, p, kk, p2, len, top;
double current = 0.0;
double step = n / 100.0;
for (k = 0; k < n; k++)
{
// Progress reporting.
if (k >= current)
{
current += step;
if (progress != null)
{
progress.Report(k / (double)n);
}
}
// compute nonzero Pattern of kth row of L, in topological order
y[k] = 0.0; // Y(0:k) is now all zero
top = n; // stack for pattern is empty
flag[k] = k; // mark node k as visited
lnz[k] = 0; // count of nonzeros in column k of L
kk = (P != null) ? (P[k]) : (k); // kth original, or permuted, column
p2 = ap[kk + 1];
for (p = ap[kk]; p < p2; p++)
{
i = (Pinv != null) ? (Pinv[ai[p]]) : (ai[p]); // get A(i,k)
if (i <= k)
{
y[i] += ax[p]; // scatter A(i,k) into Y (sum duplicates)
for (len = 0; flag[i] != k; i = parent[i])
{
pattern[len++] = i; // L(k,i) is nonzero
flag[i] = k; // mark i as visited
}
while (len > 0)
{
pattern[--top] = pattern[--len];
}
}
}
// compute numerical values kth row of L (a sparse triangular solve)
d = y[k]; // get D(k,k) and clear Y(k)
y[k] = 0.0;
for (; top < n; top++)
{
i = pattern[top]; // Pattern [top:n-1] is pattern of L(k,:)
yi = y[i]; // get and clear Y(i)
y[i] = 0.0;
p2 = lp[i] + lnz[i];
for (p = lp[i]; p < p2; p++)
{
y[li[p]] -= lx[p] * yi;
}
l_ki = yi / D[i]; // the nonzero entry L(k,i)
d -= (yi * Complex.Conjugate(yi)) / D[i];
li[p] = k; // store L(k,i) in column form of L
lx[p] = Complex.Conjugate(l_ki);
lnz[i]++; // increment count of nonzeros in col i
}
if (d.Imaginary != 0.0)
{
throw new Exception("Matrix must be Hermitian.");
}
if (d == 0.0)
{
// failure, D(k,k) is zero
throw new Exception("Diagonal element is zero.");
}
D[k] = d.Real;
}
}
}
}