1 /* linalg/luc.c
2  *
3  * Copyright (C) 2001, 2007, 2009 Brian Gough
4  * Copyright (C) 2019 Patrick Alken
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or (at
9  * your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include <config.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <gsl/gsl_math.h>
25 #include <gsl/gsl_vector.h>
26 #include <gsl/gsl_matrix.h>
27 #include <gsl/gsl_complex.h>
28 #include <gsl/gsl_complex_math.h>
29 #include <gsl/gsl_permute_vector.h>
30 #include <gsl/gsl_blas.h>
31 #include <gsl/gsl_complex_math.h>
32 #include <gsl/gsl_linalg.h>
33 
34 #include "recurse.h"
35 
36 static int LU_decomp_L2 (gsl_matrix_complex * A, gsl_vector_uint * ipiv);
37 static int LU_decomp_L3 (gsl_matrix_complex * A, gsl_vector_uint * ipiv);
38 static int singular (const gsl_matrix_complex * LU);
39 static int apply_pivots(gsl_matrix_complex * A, const gsl_vector_uint * ipiv);
40 
41 /* Factorise a general N x N complex matrix A into,
42  *
43  *   P A = L U
44  *
45  * where P is a permutation matrix, L is unit lower triangular and U
46  * is upper triangular.
47  *
48  * L is stored in the strict lower triangular part of the input
49  * matrix. The diagonal elements of L are unity and are not stored.
50  *
51  * U is stored in the diagonal and upper triangular part of the
52  * input matrix.
53  *
54  * P is stored in the permutation p. Column j of P is column k of the
55  * identity matrix, where k = permutation->data[j]
56  *
57  * signum gives the sign of the permutation, (-1)^n, where n is the
58  * number of interchanges in the permutation.
59  *
60  * See Golub & Van Loan, Matrix Computations, Algorithm 3.4.1 (Gauss
61  * Elimination with Partial Pivoting).
62  */
63 
64 int
gsl_linalg_complex_LU_decomp(gsl_matrix_complex * A,gsl_permutation * p,int * signum)65 gsl_linalg_complex_LU_decomp (gsl_matrix_complex * A, gsl_permutation * p, int *signum)
66 {
67   const size_t M = A->size1;
68 
69   if (p->size != M)
70     {
71       GSL_ERROR ("permutation length must match matrix size1", GSL_EBADLEN);
72     }
73   else
74     {
75       int status;
76       const size_t N = A->size2;
77       const size_t minMN = GSL_MIN(M, N);
78       gsl_vector_uint * ipiv = gsl_vector_uint_alloc(minMN);
79       gsl_matrix_complex_view AL = gsl_matrix_complex_submatrix(A, 0, 0, M, minMN);
80       size_t i;
81 
82       status = LU_decomp_L3 (&AL.matrix, ipiv);
83 
84       /* process remaining right matrix */
85       if (M < N)
86         {
87           gsl_matrix_complex_view AR = gsl_matrix_complex_submatrix(A, 0, M, M, N - M);
88 
89           /* apply pivots to AR */
90           apply_pivots(&AR.matrix, ipiv);
91 
92           /* AR = AL^{-1} AR */
93           gsl_blas_ztrsm(CblasLeft, CblasLower, CblasNoTrans, CblasUnit, GSL_COMPLEX_ONE, &AL.matrix, &AR.matrix);
94         }
95 
96       /* convert ipiv array to permutation */
97 
98       gsl_permutation_init(p);
99       *signum = 1;
100 
101       for (i = 0; i < minMN; ++i)
102         {
103           unsigned int pivi = gsl_vector_uint_get(ipiv, i);
104 
105           if (p->data[pivi] != p->data[i])
106             {
107               size_t tmp = p->data[pivi];
108               p->data[pivi] = p->data[i];
109               p->data[i] = tmp;
110               *signum = -(*signum);
111             }
112         }
113 
114       gsl_vector_uint_free(ipiv);
115 
116       return status;
117     }
118 }
119 
120 /*
121 LU_decomp_L2
122   LU decomposition with partial pivoting using Level 2 BLAS
123 
124 Inputs: A    - on input, matrix to be factored; on output, L and U factors
125         ipiv - (output) array containing row swaps
126 
127 Notes:
128 1) Based on LAPACK ZGETF2
129 */
130 
131 static int
LU_decomp_L2(gsl_matrix_complex * A,gsl_vector_uint * ipiv)132 LU_decomp_L2 (gsl_matrix_complex * A, gsl_vector_uint * ipiv)
133 {
134   const size_t M = A->size1;
135   const size_t N = A->size2;
136   const size_t minMN = GSL_MIN(M, N);
137 
138   if (ipiv->size != minMN)
139     {
140       GSL_ERROR ("ipiv length must equal MIN(M,N)", GSL_EBADLEN);
141     }
142   else
143     {
144       size_t i, j;
145 
146       for (j = 0; j < minMN; ++j)
147         {
148           /* find maximum in the j-th column */
149           gsl_vector_complex_view v = gsl_matrix_complex_subcolumn(A, j, j, M - j);
150           size_t j_pivot = j + gsl_blas_izamax(&v.vector);
151           gsl_vector_complex_view v1, v2;
152 
153           gsl_vector_uint_set(ipiv, j, j_pivot);
154 
155           if (j_pivot != j)
156             {
157               /* swap rows j and j_pivot */
158               v1 = gsl_matrix_complex_row(A, j);
159               v2 = gsl_matrix_complex_row(A, j_pivot);
160               gsl_blas_zswap(&v1.vector, &v2.vector);
161             }
162 
163           if (j < M - 1)
164             {
165               gsl_complex Ajj = gsl_matrix_complex_get(A, j, j);
166               gsl_complex Ajjinv = gsl_complex_inverse(Ajj);
167 
168               if (gsl_complex_abs(Ajj) >= GSL_DBL_MIN)
169                 {
170                   v1 = gsl_matrix_complex_subcolumn(A, j, j + 1, M - j - 1);
171                   gsl_blas_zscal(Ajjinv, &v1.vector);
172                 }
173               else
174                 {
175                   for (i = 1; i < M - j; ++i)
176                     {
177                       gsl_complex * ptr = gsl_matrix_complex_ptr(A, j + i, j);
178                       *ptr = gsl_complex_mul(*ptr, Ajjinv);
179                     }
180                 }
181             }
182 
183           if (j < minMN - 1)
184             {
185               gsl_matrix_complex_view A22 = gsl_matrix_complex_submatrix(A, j + 1, j + 1, M - j - 1, N - j - 1);
186               v1 = gsl_matrix_complex_subcolumn(A, j, j + 1, M - j - 1);
187               v2 = gsl_matrix_complex_subrow(A, j, j + 1, N - j - 1);
188 
189               gsl_blas_zgeru(GSL_COMPLEX_NEGONE, &v1.vector, &v2.vector, &A22.matrix);
190             }
191         }
192 
193       return GSL_SUCCESS;
194     }
195 }
196 
197 /*
198 LU_decomp_L3
199   LU decomposition with partial pivoting using Level 3 BLAS
200 
201 Inputs: A    - on input, matrix to be factored; on output, L and U factors
202         ipiv - (output) array containing row swaps
203 
204 Notes:
205 1) Based on ReLAPACK DGETRF
206 */
207 
208 static int
LU_decomp_L3(gsl_matrix_complex * A,gsl_vector_uint * ipiv)209 LU_decomp_L3 (gsl_matrix_complex * A, gsl_vector_uint * ipiv)
210 {
211   const size_t M = A->size1;
212   const size_t N = A->size2;
213 
214   if (M < N)
215     {
216       GSL_ERROR ("matrix must have M >= N", GSL_EBADLEN);
217     }
218   else if (ipiv->size != GSL_MIN(M, N))
219     {
220       GSL_ERROR ("ipiv length must equal MIN(M,N)", GSL_EBADLEN);
221     }
222   else if (N <= CROSSOVER_LU)
223     {
224       /* use Level 2 algorithm */
225       return LU_decomp_L2(A, ipiv);
226     }
227   else
228     {
229       /*
230        * partition matrix:
231        *
232        *       N1  N2
233        * N1  [ A11 A12 ]
234        * M2  [ A21 A22 ]
235        *
236        * and
237        *      N1  N2
238        * M  [ AL  AR  ]
239        */
240       int status;
241       const size_t N1 = GSL_LINALG_SPLIT_COMPLEX(N);
242       const size_t N2 = N - N1;
243       const size_t M2 = M - N1;
244       gsl_matrix_complex_view A11 = gsl_matrix_complex_submatrix(A, 0, 0, N1, N1);
245       gsl_matrix_complex_view A12 = gsl_matrix_complex_submatrix(A, 0, N1, N1, N2);
246       gsl_matrix_complex_view A21 = gsl_matrix_complex_submatrix(A, N1, 0, M2, N1);
247       gsl_matrix_complex_view A22 = gsl_matrix_complex_submatrix(A, N1, N1, M2, N2);
248 
249       gsl_matrix_complex_view AL = gsl_matrix_complex_submatrix(A, 0, 0, M, N1);
250       gsl_matrix_complex_view AR = gsl_matrix_complex_submatrix(A, 0, N1, M, N2);
251 
252       /*
253        * partition ipiv = [ ipiv1 ] N1
254        *                  [ ipiv2 ] N2
255        */
256       gsl_vector_uint_view ipiv1 = gsl_vector_uint_subvector(ipiv, 0, N1);
257       gsl_vector_uint_view ipiv2 = gsl_vector_uint_subvector(ipiv, N1, N2);
258 
259       size_t i;
260 
261       /* recursion on (AL, ipiv1) */
262       status = LU_decomp_L3(&AL.matrix, &ipiv1.vector);
263       if (status)
264         return status;
265 
266       /* apply ipiv1 to AR */
267       apply_pivots(&AR.matrix, &ipiv1.vector);
268 
269       /* A12 = A11^{-1} A12 */
270       gsl_blas_ztrsm(CblasLeft, CblasLower, CblasNoTrans, CblasUnit, GSL_COMPLEX_ONE, &A11.matrix, &A12.matrix);
271 
272       /* A22 = A22 - A21 * A12 */
273       gsl_blas_zgemm(CblasNoTrans, CblasNoTrans, GSL_COMPLEX_NEGONE, &A21.matrix, &A12.matrix, GSL_COMPLEX_ONE, &A22.matrix);
274 
275       /* recursion on (A22, ipiv2) */
276       status = LU_decomp_L3(&A22.matrix, &ipiv2.vector);
277       if (status)
278         return status;
279 
280       /* apply pivots to A21 */
281       apply_pivots(&A21.matrix, &ipiv2.vector);
282 
283       /* shift pivots */
284       for (i = 0; i < N2; ++i)
285         {
286           unsigned int * ptr = gsl_vector_uint_ptr(&ipiv2.vector, i);
287           *ptr += N1;
288         }
289 
290       return GSL_SUCCESS;
291     }
292 }
293 
294 int
gsl_linalg_complex_LU_solve(const gsl_matrix_complex * LU,const gsl_permutation * p,const gsl_vector_complex * b,gsl_vector_complex * x)295 gsl_linalg_complex_LU_solve (const gsl_matrix_complex * LU, const gsl_permutation * p, const gsl_vector_complex * b, gsl_vector_complex * x)
296 {
297   if (LU->size1 != LU->size2)
298     {
299       GSL_ERROR ("LU matrix must be square", GSL_ENOTSQR);
300     }
301   else if (LU->size1 != p->size)
302     {
303       GSL_ERROR ("permutation length must match matrix size", GSL_EBADLEN);
304     }
305   else if (LU->size1 != b->size)
306     {
307       GSL_ERROR ("matrix size must match b size", GSL_EBADLEN);
308     }
309   else if (LU->size2 != x->size)
310     {
311       GSL_ERROR ("matrix size must match solution size", GSL_EBADLEN);
312     }
313   else if (singular (LU))
314     {
315       GSL_ERROR ("matrix is singular", GSL_EDOM);
316     }
317   else
318     {
319       int status;
320 
321       /* copy x <- b */
322       gsl_vector_complex_memcpy (x, b);
323 
324       /* solve for x */
325       status = gsl_linalg_complex_LU_svx (LU, p, x);
326 
327       return status;
328     }
329 }
330 
331 
332 int
gsl_linalg_complex_LU_svx(const gsl_matrix_complex * LU,const gsl_permutation * p,gsl_vector_complex * x)333 gsl_linalg_complex_LU_svx (const gsl_matrix_complex * LU, const gsl_permutation * p, gsl_vector_complex * x)
334 {
335   if (LU->size1 != LU->size2)
336     {
337       GSL_ERROR ("LU matrix must be square", GSL_ENOTSQR);
338     }
339   else if (LU->size1 != p->size)
340     {
341       GSL_ERROR ("permutation length must match matrix size", GSL_EBADLEN);
342     }
343   else if (LU->size1 != x->size)
344     {
345       GSL_ERROR ("matrix size must match solution/rhs size", GSL_EBADLEN);
346     }
347   else if (singular (LU))
348     {
349       GSL_ERROR ("matrix is singular", GSL_EDOM);
350     }
351   else
352     {
353       /* apply permutation to RHS */
354       gsl_permute_vector_complex (p, x);
355 
356       /* solve for c using forward-substitution, L c = P b */
357       gsl_blas_ztrsv (CblasLower, CblasNoTrans, CblasUnit, LU, x);
358 
359       /* perform back-substitution, U x = c */
360       gsl_blas_ztrsv (CblasUpper, CblasNoTrans, CblasNonUnit, LU, x);
361 
362       return GSL_SUCCESS;
363     }
364 }
365 
366 
367 int
gsl_linalg_complex_LU_refine(const gsl_matrix_complex * A,const gsl_matrix_complex * LU,const gsl_permutation * p,const gsl_vector_complex * b,gsl_vector_complex * x,gsl_vector_complex * work)368 gsl_linalg_complex_LU_refine (const gsl_matrix_complex * A, const gsl_matrix_complex * LU, const gsl_permutation * p, const gsl_vector_complex * b, gsl_vector_complex * x, gsl_vector_complex * work)
369 {
370   if (A->size1 != A->size2)
371     {
372       GSL_ERROR ("matrix a must be square", GSL_ENOTSQR);
373     }
374   if (LU->size1 != LU->size2)
375     {
376       GSL_ERROR ("LU matrix must be square", GSL_ENOTSQR);
377     }
378   else if (A->size1 != LU->size2)
379     {
380       GSL_ERROR ("LU matrix must be decomposition of a", GSL_ENOTSQR);
381     }
382   else if (LU->size1 != p->size)
383     {
384       GSL_ERROR ("permutation length must match matrix size", GSL_EBADLEN);
385     }
386   else if (LU->size1 != b->size)
387     {
388       GSL_ERROR ("matrix size must match b size", GSL_EBADLEN);
389     }
390   else if (LU->size1 != x->size)
391     {
392       GSL_ERROR ("matrix size must match solution size", GSL_EBADLEN);
393     }
394   else if (LU->size1 != work->size)
395     {
396       GSL_ERROR ("matrix size must match workspace size", GSL_EBADLEN);
397     }
398   else if (singular (LU))
399     {
400       GSL_ERROR ("matrix is singular", GSL_EDOM);
401     }
402   else
403     {
404       int status;
405 
406       /* Compute residual = (A * x  - b) */
407 
408       gsl_vector_complex_memcpy (work, b);
409 
410       {
411         gsl_complex one = GSL_COMPLEX_ONE;
412         gsl_complex negone = GSL_COMPLEX_NEGONE;
413         gsl_blas_zgemv (CblasNoTrans, one, A, x, negone, work);
414       }
415 
416       /* Find correction, delta = - (A^-1) * residual, and apply it */
417 
418       status = gsl_linalg_complex_LU_svx (LU, p, work);
419 
420       {
421         gsl_complex negone= GSL_COMPLEX_NEGONE;
422         gsl_blas_zaxpy (negone, work, x);
423       }
424 
425       return status;
426     }
427 }
428 
429 int
gsl_linalg_complex_LU_invert(const gsl_matrix_complex * LU,const gsl_permutation * p,gsl_matrix_complex * inverse)430 gsl_linalg_complex_LU_invert (const gsl_matrix_complex * LU, const gsl_permutation * p, gsl_matrix_complex * inverse)
431 {
432   if (LU->size1 != LU->size2)
433     {
434       GSL_ERROR ("LU matrix must be square", GSL_ENOTSQR);
435     }
436   else if (LU->size1 != p->size)
437     {
438       GSL_ERROR ("permutation length must match matrix size", GSL_EBADLEN);
439     }
440   else if (inverse->size1 != LU->size1 || inverse->size2 != LU->size2)
441     {
442       GSL_ERROR ("inverse matrix must match LU matrix dimensions", GSL_EBADLEN);
443     }
444   else
445     {
446       gsl_matrix_complex_memcpy(inverse, LU);
447       return gsl_linalg_complex_LU_invx(inverse, p);
448     }
449 }
450 
451 int
gsl_linalg_complex_LU_invx(gsl_matrix_complex * LU,const gsl_permutation * p)452 gsl_linalg_complex_LU_invx (gsl_matrix_complex * LU, const gsl_permutation * p)
453 {
454   if (LU->size1 != LU->size2)
455     {
456       GSL_ERROR ("LU matrix must be square", GSL_ENOTSQR);
457     }
458   else if (LU->size1 != p->size)
459     {
460       GSL_ERROR ("permutation length must match matrix size", GSL_EBADLEN);
461     }
462   else if (singular (LU))
463     {
464       GSL_ERROR ("matrix is singular", GSL_EDOM);
465     }
466   else
467     {
468       int status;
469       const size_t N = LU->size1;
470       size_t i;
471 
472       /* compute U^{-1} */
473       status = gsl_linalg_complex_tri_invert(CblasUpper, CblasNonUnit, LU);
474       if (status)
475         return status;
476 
477       /* compute L^{-1} */
478       status = gsl_linalg_complex_tri_invert(CblasLower, CblasUnit, LU);
479       if (status)
480         return status;
481 
482       /* compute U^{-1} L^{-1} */
483       status = gsl_linalg_complex_tri_UL(LU);
484       if (status)
485         return status;
486 
487       /* apply permutation to columns of A^{-1} */
488       for (i = 0; i < N; ++i)
489         {
490           gsl_vector_complex_view v = gsl_matrix_complex_row(LU, i);
491           gsl_permute_vector_complex_inverse(p, &v.vector);
492         }
493 
494       return GSL_SUCCESS;
495     }
496 }
497 
498 gsl_complex
gsl_linalg_complex_LU_det(gsl_matrix_complex * LU,int signum)499 gsl_linalg_complex_LU_det (gsl_matrix_complex * LU, int signum)
500 {
501   size_t i, n = LU->size1;
502 
503   gsl_complex det = gsl_complex_rect((double) signum, 0.0);
504 
505   for (i = 0; i < n; i++)
506     {
507       gsl_complex zi = gsl_matrix_complex_get (LU, i, i);
508       det = gsl_complex_mul (det, zi);
509     }
510 
511   return det;
512 }
513 
514 
515 double
gsl_linalg_complex_LU_lndet(gsl_matrix_complex * LU)516 gsl_linalg_complex_LU_lndet (gsl_matrix_complex * LU)
517 {
518   size_t i, n = LU->size1;
519 
520   double lndet = 0.0;
521 
522   for (i = 0; i < n; i++)
523     {
524       gsl_complex z = gsl_matrix_complex_get (LU, i, i);
525       lndet += log (gsl_complex_abs (z));
526     }
527 
528   return lndet;
529 }
530 
531 
532 gsl_complex
gsl_linalg_complex_LU_sgndet(gsl_matrix_complex * LU,int signum)533 gsl_linalg_complex_LU_sgndet (gsl_matrix_complex * LU, int signum)
534 {
535   size_t i, n = LU->size1;
536 
537   gsl_complex phase = gsl_complex_rect((double) signum, 0.0);
538 
539   for (i = 0; i < n; i++)
540     {
541       gsl_complex z = gsl_matrix_complex_get (LU, i, i);
542 
543       double r = gsl_complex_abs(z);
544 
545       if (r == 0)
546         {
547           phase = gsl_complex_rect(0.0, 0.0);
548           break;
549         }
550       else
551         {
552           z = gsl_complex_div_real(z, r);
553           phase = gsl_complex_mul(phase, z);
554         }
555     }
556 
557   return phase;
558 }
559 
560 static int
singular(const gsl_matrix_complex * LU)561 singular (const gsl_matrix_complex * LU)
562 {
563   size_t i, n = LU->size1;
564 
565   for (i = 0; i < n; i++)
566     {
567       gsl_complex u = gsl_matrix_complex_get (LU, i, i);
568       if (GSL_REAL(u) == 0 && GSL_IMAG(u) == 0) return 1;
569     }
570 
571  return 0;
572 }
573 
574 static int
apply_pivots(gsl_matrix_complex * A,const gsl_vector_uint * ipiv)575 apply_pivots(gsl_matrix_complex * A, const gsl_vector_uint * ipiv)
576 {
577   if (A->size1 < ipiv->size)
578     {
579       GSL_ERROR("matrix does not match pivot vector", GSL_EBADLEN);
580     }
581   else
582     {
583       size_t i;
584 
585       for (i = 0; i < ipiv->size; ++i)
586         {
587           size_t pi = gsl_vector_uint_get(ipiv, i);
588 
589           if (i != pi)
590             {
591               /* swap rows i and pi */
592               gsl_vector_complex_view v1 = gsl_matrix_complex_row(A, i);
593               gsl_vector_complex_view v2 = gsl_matrix_complex_row(A, pi);
594               gsl_blas_zswap(&v1.vector, &v2.vector);
595             }
596         }
597 
598       return GSL_SUCCESS;
599     }
600 }
601