1: *> \brief \b DSYTRI2X
2: *
3: * =========== DOCUMENTATION ===========
4: *
5: * Online html documentation available at
6: * http://www.netlib.org/lapack/explore-html/
7: *
8: *> \htmlonly
9: *> Download DSYTRI2X + dependencies
10: *> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dsytri2x.f">
11: *> [TGZ]</a>
12: *> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dsytri2x.f">
13: *> [ZIP]</a>
14: *> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dsytri2x.f">
15: *> [TXT]</a>
16: *> \endhtmlonly
17: *
18: * Definition:
19: * ===========
20: *
21: * SUBROUTINE DSYTRI2X( UPLO, N, A, LDA, IPIV, WORK, NB, INFO )
22: *
23: * .. Scalar Arguments ..
24: * CHARACTER UPLO
25: * INTEGER INFO, LDA, N, NB
26: * ..
27: * .. Array Arguments ..
28: * INTEGER IPIV( * )
29: * DOUBLE PRECISION A( LDA, * ), WORK( N+NB+1,* )
30: * ..
31: *
32: *
33: *> \par Purpose:
34: * =============
35: *>
36: *> \verbatim
37: *>
38: *> DSYTRI2X computes the inverse of a real symmetric indefinite matrix
39: *> A using the factorization A = U*D*U**T or A = L*D*L**T computed by
40: *> DSYTRF.
41: *> \endverbatim
42: *
43: * Arguments:
44: * ==========
45: *
46: *> \param[in] UPLO
47: *> \verbatim
48: *> UPLO is CHARACTER*1
49: *> Specifies whether the details of the factorization are stored
50: *> as an upper or lower triangular matrix.
51: *> = 'U': Upper triangular, form is A = U*D*U**T;
52: *> = 'L': Lower triangular, form is A = L*D*L**T.
53: *> \endverbatim
54: *>
55: *> \param[in] N
56: *> \verbatim
57: *> N is INTEGER
58: *> The order of the matrix A. N >= 0.
59: *> \endverbatim
60: *>
61: *> \param[in,out] A
62: *> \verbatim
63: *> A is DOUBLE PRECISION array, dimension (LDA,N)
64: *> On entry, the NNB diagonal matrix D and the multipliers
65: *> used to obtain the factor U or L as computed by DSYTRF.
66: *>
67: *> On exit, if INFO = 0, the (symmetric) inverse of the original
68: *> matrix. If UPLO = 'U', the upper triangular part of the
69: *> inverse is formed and the part of A below the diagonal is not
70: *> referenced; if UPLO = 'L' the lower triangular part of the
71: *> inverse is formed and the part of A above the diagonal is
72: *> not referenced.
73: *> \endverbatim
74: *>
75: *> \param[in] LDA
76: *> \verbatim
77: *> LDA is INTEGER
78: *> The leading dimension of the array A. LDA >= max(1,N).
79: *> \endverbatim
80: *>
81: *> \param[in] IPIV
82: *> \verbatim
83: *> IPIV is INTEGER array, dimension (N)
84: *> Details of the interchanges and the NNB structure of D
85: *> as determined by DSYTRF.
86: *> \endverbatim
87: *>
88: *> \param[out] WORK
89: *> \verbatim
90: *> WORK is DOUBLE PRECISION array, dimension (N+NB+1,NB+3)
91: *> \endverbatim
92: *>
93: *> \param[in] NB
94: *> \verbatim
95: *> NB is INTEGER
96: *> Block size
97: *> \endverbatim
98: *>
99: *> \param[out] INFO
100: *> \verbatim
101: *> INFO is INTEGER
102: *> = 0: successful exit
103: *> < 0: if INFO = -i, the i-th argument had an illegal value
104: *> > 0: if INFO = i, D(i,i) = 0; the matrix is singular and its
105: *> inverse could not be computed.
106: *> \endverbatim
107: *
108: * Authors:
109: * ========
110: *
111: *> \author Univ. of Tennessee
112: *> \author Univ. of California Berkeley
113: *> \author Univ. of Colorado Denver
114: *> \author NAG Ltd.
115: *
116: *> \ingroup doubleSYcomputational
117: *
118: * =====================================================================
119: SUBROUTINE DSYTRI2X( UPLO, N, A, LDA, IPIV, WORK, NB, INFO )
120: *
121: * -- LAPACK computational routine --
122: * -- LAPACK is a software package provided by Univ. of Tennessee, --
123: * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--
124: *
125: * .. Scalar Arguments ..
126: CHARACTER UPLO
127: INTEGER INFO, LDA, N, NB
128: * ..
129: * .. Array Arguments ..
130: INTEGER IPIV( * )
131: DOUBLE PRECISION A( LDA, * ), WORK( N+NB+1,* )
132: * ..
133: *
134: * =====================================================================
135: *
136: * .. Parameters ..
137: DOUBLE PRECISION ONE, ZERO
138: PARAMETER ( ONE = 1.0D+0, ZERO = 0.0D+0 )
139: * ..
140: * .. Local Scalars ..
141: LOGICAL UPPER
142: INTEGER I, IINFO, IP, K, CUT, NNB
143: INTEGER COUNT
144: INTEGER J, U11, INVD
145:
146: DOUBLE PRECISION AK, AKKP1, AKP1, D, T
147: DOUBLE PRECISION U01_I_J, U01_IP1_J
148: DOUBLE PRECISION U11_I_J, U11_IP1_J
149: * ..
150: * .. External Functions ..
151: LOGICAL LSAME
152: EXTERNAL LSAME
153: * ..
154: * .. External Subroutines ..
155: EXTERNAL DSYCONV, XERBLA, DTRTRI
156: EXTERNAL DGEMM, DTRMM, DSYSWAPR
157: * ..
158: * .. Intrinsic Functions ..
159: INTRINSIC MAX
160: * ..
161: * .. Executable Statements ..
162: *
163: * Test the input parameters.
164: *
165: INFO = 0
166: UPPER = LSAME( UPLO, 'U' )
167: IF( .NOT.UPPER .AND. .NOT.LSAME( UPLO, 'L' ) ) THEN
168: INFO = -1
169: ELSE IF( N.LT.0 ) THEN
170: INFO = -2
171: ELSE IF( LDA.LT.MAX( 1, N ) ) THEN
172: INFO = -4
173: END IF
174: *
175: * Quick return if possible
176: *
177: *
178: IF( INFO.NE.0 ) THEN
179: CALL XERBLA( 'DSYTRI2X', -INFO )
180: RETURN
181: END IF
182: IF( N.EQ.0 )
183: $ RETURN
184: *
185: * Convert A
186: * Workspace got Non-diag elements of D
187: *
188: CALL DSYCONV( UPLO, 'C', N, A, LDA, IPIV, WORK, IINFO )
189: *
190: * Check that the diagonal matrix D is nonsingular.
191: *
192: IF( UPPER ) THEN
193: *
194: * Upper triangular storage: examine D from bottom to top
195: *
196: DO INFO = N, 1, -1
197: IF( IPIV( INFO ).GT.0 .AND. A( INFO, INFO ).EQ.ZERO )
198: $ RETURN
199: END DO
200: ELSE
201: *
202: * Lower triangular storage: examine D from top to bottom.
203: *
204: DO INFO = 1, N
205: IF( IPIV( INFO ).GT.0 .AND. A( INFO, INFO ).EQ.ZERO )
206: $ RETURN
207: END DO
208: END IF
209: INFO = 0
210: *
211: * Splitting Workspace
212: * U01 is a block (N,NB+1)
213: * The first element of U01 is in WORK(1,1)
214: * U11 is a block (NB+1,NB+1)
215: * The first element of U11 is in WORK(N+1,1)
216: U11 = N
217: * INVD is a block (N,2)
218: * The first element of INVD is in WORK(1,INVD)
219: INVD = NB+2
220:
221: IF( UPPER ) THEN
222: *
223: * invA = P * inv(U**T)*inv(D)*inv(U)*P**T.
224: *
225: CALL DTRTRI( UPLO, 'U', N, A, LDA, INFO )
226: *
227: * inv(D) and inv(D)*inv(U)
228: *
229: K=1
230: DO WHILE ( K .LE. N )
231: IF( IPIV( K ).GT.0 ) THEN
232: * 1 x 1 diagonal NNB
233: WORK(K,INVD) = ONE / A( K, K )
234: WORK(K,INVD+1) = 0
235: K=K+1
236: ELSE
237: * 2 x 2 diagonal NNB
238: T = WORK(K+1,1)
239: AK = A( K, K ) / T
240: AKP1 = A( K+1, K+1 ) / T
241: AKKP1 = WORK(K+1,1) / T
242: D = T*( AK*AKP1-ONE )
243: WORK(K,INVD) = AKP1 / D
244: WORK(K+1,INVD+1) = AK / D
245: WORK(K,INVD+1) = -AKKP1 / D
246: WORK(K+1,INVD) = -AKKP1 / D
247: K=K+2
248: END IF
249: END DO
250: *
251: * inv(U**T) = (inv(U))**T
252: *
253: * inv(U**T)*inv(D)*inv(U)
254: *
255: CUT=N
256: DO WHILE (CUT .GT. 0)
257: NNB=NB
258: IF (CUT .LE. NNB) THEN
259: NNB=CUT
260: ELSE
261: COUNT = 0
262: * count negative elements,
263: DO I=CUT+1-NNB,CUT
264: IF (IPIV(I) .LT. 0) COUNT=COUNT+1
265: END DO
266: * need a even number for a clear cut
267: IF (MOD(COUNT,2) .EQ. 1) NNB=NNB+1
268: END IF
269:
270: CUT=CUT-NNB
271: *
272: * U01 Block
273: *
274: DO I=1,CUT
275: DO J=1,NNB
276: WORK(I,J)=A(I,CUT+J)
277: END DO
278: END DO
279: *
280: * U11 Block
281: *
282: DO I=1,NNB
283: WORK(U11+I,I)=ONE
284: DO J=1,I-1
285: WORK(U11+I,J)=ZERO
286: END DO
287: DO J=I+1,NNB
288: WORK(U11+I,J)=A(CUT+I,CUT+J)
289: END DO
290: END DO
291: *
292: * invD*U01
293: *
294: I=1
295: DO WHILE (I .LE. CUT)
296: IF (IPIV(I) > 0) THEN
297: DO J=1,NNB
298: WORK(I,J)=WORK(I,INVD)*WORK(I,J)
299: END DO
300: I=I+1
301: ELSE
302: DO J=1,NNB
303: U01_I_J = WORK(I,J)
304: U01_IP1_J = WORK(I+1,J)
305: WORK(I,J)=WORK(I,INVD)*U01_I_J+
306: $ WORK(I,INVD+1)*U01_IP1_J
307: WORK(I+1,J)=WORK(I+1,INVD)*U01_I_J+
308: $ WORK(I+1,INVD+1)*U01_IP1_J
309: END DO
310: I=I+2
311: END IF
312: END DO
313: *
314: * invD1*U11
315: *
316: I=1
317: DO WHILE (I .LE. NNB)
318: IF (IPIV(CUT+I) > 0) THEN
319: DO J=I,NNB
320: WORK(U11+I,J)=WORK(CUT+I,INVD)*WORK(U11+I,J)
321: END DO
322: I=I+1
323: ELSE
324: DO J=I,NNB
325: U11_I_J = WORK(U11+I,J)
326: U11_IP1_J = WORK(U11+I+1,J)
327: WORK(U11+I,J)=WORK(CUT+I,INVD)*WORK(U11+I,J) +
328: $ WORK(CUT+I,INVD+1)*WORK(U11+I+1,J)
329: WORK(U11+I+1,J)=WORK(CUT+I+1,INVD)*U11_I_J+
330: $ WORK(CUT+I+1,INVD+1)*U11_IP1_J
331: END DO
332: I=I+2
333: END IF
334: END DO
335: *
336: * U11**T*invD1*U11->U11
337: *
338: CALL DTRMM('L','U','T','U',NNB, NNB,
339: $ ONE,A(CUT+1,CUT+1),LDA,WORK(U11+1,1),N+NB+1)
340: *
341: DO I=1,NNB
342: DO J=I,NNB
343: A(CUT+I,CUT+J)=WORK(U11+I,J)
344: END DO
345: END DO
346: *
347: * U01**T*invD*U01->A(CUT+I,CUT+J)
348: *
349: CALL DGEMM('T','N',NNB,NNB,CUT,ONE,A(1,CUT+1),LDA,
350: $ WORK,N+NB+1, ZERO, WORK(U11+1,1), N+NB+1)
351:
352: *
353: * U11 = U11**T*invD1*U11 + U01**T*invD*U01
354: *
355: DO I=1,NNB
356: DO J=I,NNB
357: A(CUT+I,CUT+J)=A(CUT+I,CUT+J)+WORK(U11+I,J)
358: END DO
359: END DO
360: *
361: * U01 = U00**T*invD0*U01
362: *
363: CALL DTRMM('L',UPLO,'T','U',CUT, NNB,
364: $ ONE,A,LDA,WORK,N+NB+1)
365:
366: *
367: * Update U01
368: *
369: DO I=1,CUT
370: DO J=1,NNB
371: A(I,CUT+J)=WORK(I,J)
372: END DO
373: END DO
374: *
375: * Next Block
376: *
377: END DO
378: *
379: * Apply PERMUTATIONS P and P**T: P * inv(U**T)*inv(D)*inv(U) *P**T
380: *
381: I=1
382: DO WHILE ( I .LE. N )
383: IF( IPIV(I) .GT. 0 ) THEN
384: IP=IPIV(I)
385: IF (I .LT. IP) CALL DSYSWAPR( UPLO, N, A, LDA, I ,IP )
386: IF (I .GT. IP) CALL DSYSWAPR( UPLO, N, A, LDA, IP ,I )
387: ELSE
388: IP=-IPIV(I)
389: I=I+1
390: IF ( (I-1) .LT. IP)
391: $ CALL DSYSWAPR( UPLO, N, A, LDA, I-1 ,IP )
392: IF ( (I-1) .GT. IP)
393: $ CALL DSYSWAPR( UPLO, N, A, LDA, IP ,I-1 )
394: ENDIF
395: I=I+1
396: END DO
397: ELSE
398: *
399: * LOWER...
400: *
401: * invA = P * inv(U**T)*inv(D)*inv(U)*P**T.
402: *
403: CALL DTRTRI( UPLO, 'U', N, A, LDA, INFO )
404: *
405: * inv(D) and inv(D)*inv(U)
406: *
407: K=N
408: DO WHILE ( K .GE. 1 )
409: IF( IPIV( K ).GT.0 ) THEN
410: * 1 x 1 diagonal NNB
411: WORK(K,INVD) = ONE / A( K, K )
412: WORK(K,INVD+1) = 0
413: K=K-1
414: ELSE
415: * 2 x 2 diagonal NNB
416: T = WORK(K-1,1)
417: AK = A( K-1, K-1 ) / T
418: AKP1 = A( K, K ) / T
419: AKKP1 = WORK(K-1,1) / T
420: D = T*( AK*AKP1-ONE )
421: WORK(K-1,INVD) = AKP1 / D
422: WORK(K,INVD) = AK / D
423: WORK(K,INVD+1) = -AKKP1 / D
424: WORK(K-1,INVD+1) = -AKKP1 / D
425: K=K-2
426: END IF
427: END DO
428: *
429: * inv(U**T) = (inv(U))**T
430: *
431: * inv(U**T)*inv(D)*inv(U)
432: *
433: CUT=0
434: DO WHILE (CUT .LT. N)
435: NNB=NB
436: IF (CUT + NNB .GT. N) THEN
437: NNB=N-CUT
438: ELSE
439: COUNT = 0
440: * count negative elements,
441: DO I=CUT+1,CUT+NNB
442: IF (IPIV(I) .LT. 0) COUNT=COUNT+1
443: END DO
444: * need a even number for a clear cut
445: IF (MOD(COUNT,2) .EQ. 1) NNB=NNB+1
446: END IF
447: * L21 Block
448: DO I=1,N-CUT-NNB
449: DO J=1,NNB
450: WORK(I,J)=A(CUT+NNB+I,CUT+J)
451: END DO
452: END DO
453: * L11 Block
454: DO I=1,NNB
455: WORK(U11+I,I)=ONE
456: DO J=I+1,NNB
457: WORK(U11+I,J)=ZERO
458: END DO
459: DO J=1,I-1
460: WORK(U11+I,J)=A(CUT+I,CUT+J)
461: END DO
462: END DO
463: *
464: * invD*L21
465: *
466: I=N-CUT-NNB
467: DO WHILE (I .GE. 1)
468: IF (IPIV(CUT+NNB+I) > 0) THEN
469: DO J=1,NNB
470: WORK(I,J)=WORK(CUT+NNB+I,INVD)*WORK(I,J)
471: END DO
472: I=I-1
473: ELSE
474: DO J=1,NNB
475: U01_I_J = WORK(I,J)
476: U01_IP1_J = WORK(I-1,J)
477: WORK(I,J)=WORK(CUT+NNB+I,INVD)*U01_I_J+
478: $ WORK(CUT+NNB+I,INVD+1)*U01_IP1_J
479: WORK(I-1,J)=WORK(CUT+NNB+I-1,INVD+1)*U01_I_J+
480: $ WORK(CUT+NNB+I-1,INVD)*U01_IP1_J
481: END DO
482: I=I-2
483: END IF
484: END DO
485: *
486: * invD1*L11
487: *
488: I=NNB
489: DO WHILE (I .GE. 1)
490: IF (IPIV(CUT+I) > 0) THEN
491: DO J=1,NNB
492: WORK(U11+I,J)=WORK(CUT+I,INVD)*WORK(U11+I,J)
493: END DO
494: I=I-1
495: ELSE
496: DO J=1,NNB
497: U11_I_J = WORK(U11+I,J)
498: U11_IP1_J = WORK(U11+I-1,J)
499: WORK(U11+I,J)=WORK(CUT+I,INVD)*WORK(U11+I,J) +
500: $ WORK(CUT+I,INVD+1)*U11_IP1_J
501: WORK(U11+I-1,J)=WORK(CUT+I-1,INVD+1)*U11_I_J+
502: $ WORK(CUT+I-1,INVD)*U11_IP1_J
503: END DO
504: I=I-2
505: END IF
506: END DO
507: *
508: * L11**T*invD1*L11->L11
509: *
510: CALL DTRMM('L',UPLO,'T','U',NNB, NNB,
511: $ ONE,A(CUT+1,CUT+1),LDA,WORK(U11+1,1),N+NB+1)
512:
513: *
514: DO I=1,NNB
515: DO J=1,I
516: A(CUT+I,CUT+J)=WORK(U11+I,J)
517: END DO
518: END DO
519: *
520: IF ( (CUT+NNB) .LT. N ) THEN
521: *
522: * L21**T*invD2*L21->A(CUT+I,CUT+J)
523: *
524: CALL DGEMM('T','N',NNB,NNB,N-NNB-CUT,ONE,A(CUT+NNB+1,CUT+1)
525: $ ,LDA,WORK,N+NB+1, ZERO, WORK(U11+1,1), N+NB+1)
526:
527: *
528: * L11 = L11**T*invD1*L11 + U01**T*invD*U01
529: *
530: DO I=1,NNB
531: DO J=1,I
532: A(CUT+I,CUT+J)=A(CUT+I,CUT+J)+WORK(U11+I,J)
533: END DO
534: END DO
535: *
536: * L01 = L22**T*invD2*L21
537: *
538: CALL DTRMM('L',UPLO,'T','U', N-NNB-CUT, NNB,
539: $ ONE,A(CUT+NNB+1,CUT+NNB+1),LDA,WORK,N+NB+1)
540: *
541: * Update L21
542: *
543: DO I=1,N-CUT-NNB
544: DO J=1,NNB
545: A(CUT+NNB+I,CUT+J)=WORK(I,J)
546: END DO
547: END DO
548:
549: ELSE
550: *
551: * L11 = L11**T*invD1*L11
552: *
553: DO I=1,NNB
554: DO J=1,I
555: A(CUT+I,CUT+J)=WORK(U11+I,J)
556: END DO
557: END DO
558: END IF
559: *
560: * Next Block
561: *
562: CUT=CUT+NNB
563: END DO
564: *
565: * Apply PERMUTATIONS P and P**T: P * inv(U**T)*inv(D)*inv(U) *P**T
566: *
567: I=N
568: DO WHILE ( I .GE. 1 )
569: IF( IPIV(I) .GT. 0 ) THEN
570: IP=IPIV(I)
571: IF (I .LT. IP) CALL DSYSWAPR( UPLO, N, A, LDA, I ,IP )
572: IF (I .GT. IP) CALL DSYSWAPR( UPLO, N, A, LDA, IP ,I )
573: ELSE
574: IP=-IPIV(I)
575: IF ( I .LT. IP) CALL DSYSWAPR( UPLO, N, A, LDA, I ,IP )
576: IF ( I .GT. IP) CALL DSYSWAPR( UPLO, N, A, LDA, IP, I )
577: I=I-1
578: ENDIF
579: I=I-1
580: END DO
581: END IF
582: *
583: RETURN
584: *
585: * End of DSYTRI2X
586: *
587: END
588:
CVSweb interface <joel.bertrand@systella.fr>