dynare/mex/sources/libslicot/SB03MY.f

614 lines
21 KiB
Fortran

SUBROUTINE SB03MY( TRANA, N, A, LDA, C, LDC, SCALE, INFO )
C
C SLICOT RELEASE 5.0.
C
C Copyright (c) 2002-2009 NICONET e.V.
C
C This program is free software: you can redistribute it and/or
C modify it under the terms of the GNU General Public License as
C published by the Free Software Foundation, either version 2 of
C the License, or (at your option) any later version.
C
C This program is distributed in the hope that it will be useful,
C but WITHOUT ANY WARRANTY; without even the implied warranty of
C MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
C GNU General Public License for more details.
C
C You should have received a copy of the GNU General Public License
C along with this program. If not, see
C <http://www.gnu.org/licenses/>.
C
C PURPOSE
C
C To solve the real Lyapunov matrix equation
C
C op(A)'*X + X*op(A) = scale*C
C
C where op(A) = A or A' (A**T), A is upper quasi-triangular and C is
C symmetric (C = C'). (A' denotes the transpose of the matrix A.)
C A is N-by-N, the right hand side C and the solution X are N-by-N,
C and scale is an output scale factor, set less than or equal to 1
C to avoid overflow in X. The solution matrix X is overwritten
C onto C.
C
C A must be in Schur canonical form (as returned by LAPACK routines
C DGEES or DHSEQR), that is, block upper triangular with 1-by-1 and
C 2-by-2 diagonal blocks; each 2-by-2 diagonal block has its
C diagonal elements equal and its off-diagonal elements of opposite
C sign.
C
C ARGUMENTS
C
C Mode Parameters
C
C TRANA CHARACTER*1
C Specifies the form of op(A) to be used, as follows:
C = 'N': op(A) = A (No transpose);
C = 'T': op(A) = A**T (Transpose);
C = 'C': op(A) = A**T (Conjugate transpose = Transpose).
C
C Input/Output Parameters
C
C N (input) INTEGER
C The order of the matrices A, X, and C. N >= 0.
C
C A (input) DOUBLE PRECISION array, dimension (LDA,N)
C The leading N-by-N part of this array must contain the
C upper quasi-triangular matrix A, in Schur canonical form.
C The part of A below the first sub-diagonal is not
C referenced.
C
C LDA INTEGER
C The leading dimension of array A. LDA >= MAX(1,N).
C
C C (input/output) DOUBLE PRECISION array, dimension (LDC,N)
C On entry, the leading N-by-N part of this array must
C contain the symmetric matrix C.
C On exit, if INFO >= 0, the leading N-by-N part of this
C array contains the symmetric solution matrix X.
C
C LDC INTEGER
C The leading dimension of array C. LDC >= MAX(1,N).
C
C SCALE (output) DOUBLE PRECISION
C The scale factor, scale, set less than or equal to 1 to
C prevent the solution overflowing.
C
C Error Indicator
C
C INFO INTEGER
C = 0: successful exit;
C < 0: if INFO = -i, the i-th argument had an illegal
C value;
C = 1: if A and -A have common or very close eigenvalues;
C perturbed values were used to solve the equation
C (but the matrix A is unchanged).
C
C METHOD
C
C Bartels-Stewart algorithm is used. A set of equivalent linear
C algebraic systems of equations of order at most four are formed
C and solved using Gaussian elimination with complete pivoting.
C
C REFERENCES
C
C [1] Bartels, R.H. and Stewart, G.W. T
C Solution of the matrix equation A X + XB = C.
C Comm. A.C.M., 15, pp. 820-826, 1972.
C
C NUMERICAL ASPECTS
C 3
C The algorithm requires 0(N ) operations.
C
C CONTRIBUTOR
C
C Release 3.0: V. Sima, Katholieke Univ. Leuven, Belgium, May 1997.
C Supersedes Release 2.0 routine SB03AY by Control Systems Research
C Group, Kingston Polytechnic, United Kingdom, October 1982.
C Based on DTRLYP by P. Petkov, Tech. University of Sofia, September
C 1993.
C
C REVISIONS
C
C V. Sima, Katholieke Univ. Leuven, Belgium, May 1999.
C
C KEYWORDS
C
C Continuous-time system, Lyapunov equation, matrix algebra, real
C Schur form.
C
C ******************************************************************
C
C .. Parameters ..
DOUBLE PRECISION ZERO, ONE
PARAMETER ( ZERO = 0.0D+0, ONE = 1.0D+0 )
C ..
C .. Scalar Arguments ..
CHARACTER TRANA
INTEGER INFO, LDA, LDC, N
DOUBLE PRECISION SCALE
C ..
C .. Array Arguments ..
DOUBLE PRECISION A( LDA, * ), C( LDC, * )
C ..
C .. Local Scalars ..
LOGICAL NOTRNA, LUPPER
INTEGER IERR, J, K, K1, K2, KNEXT, L, L1, L2, LNEXT,
$ MINK1N, MINK2N, MINL1N, MINL2N
DOUBLE PRECISION A11, BIGNUM, DA11, DB, EPS, SCALOC, SMIN,
$ SMLNUM, XNORM
C ..
C .. Local Arrays ..
DOUBLE PRECISION DUM( 1 ), VEC( 2, 2 ), X( 2, 2 )
C ..
C .. External Functions ..
LOGICAL LSAME
DOUBLE PRECISION DDOT, DLAMCH, DLANHS
EXTERNAL DDOT, DLAMCH, DLANHS, LSAME
C ..
C .. External Subroutines ..
EXTERNAL DLABAD, DLALN2, DLASY2, DSCAL, SB03MW, XERBLA
C ..
C .. Intrinsic Functions ..
INTRINSIC ABS, DBLE, MAX, MIN
C ..
C .. Executable Statements ..
C
C Decode and Test input parameters.
C
NOTRNA = LSAME( TRANA, 'N' )
LUPPER = .TRUE.
C
INFO = 0
IF( .NOT.NOTRNA .AND. .NOT.LSAME( TRANA, 'T' ) .AND.
$ .NOT.LSAME( TRANA, 'C' ) ) THEN
INFO = -1
ELSE IF( N.LT.0 ) THEN
INFO = -2
ELSE IF( LDA.LT.MAX( 1, N ) ) THEN
INFO = -4
ELSE IF( LDC.LT.MAX( 1, N ) ) THEN
INFO = -6
END IF
C
IF( INFO.NE.0 ) THEN
CALL XERBLA( 'SB03MY', -INFO )
RETURN
END IF
C
SCALE = ONE
C
C Quick return if possible.
C
IF( N.EQ.0 )
$ RETURN
C
C Set constants to control overflow.
C
EPS = DLAMCH( 'P' )
SMLNUM = DLAMCH( 'S' )
BIGNUM = ONE / SMLNUM
CALL DLABAD( SMLNUM, BIGNUM )
SMLNUM = SMLNUM*DBLE( N*N ) / EPS
BIGNUM = ONE / SMLNUM
C
SMIN = MAX( SMLNUM, EPS*DLANHS( 'Max', N, A, LDA, DUM ) )
C
IF( NOTRNA ) THEN
C
C Solve A'*X + X*A = scale*C.
C
C The (K,L)th block of X is determined starting from
C upper-left corner column by column by
C
C A(K,K)'*X(K,L) + X(K,L)*A(L,L) = C(K,L) - R(K,L),
C
C where
C K-1 L-1
C R(K,L) = SUM [A(I,K)'*X(I,L)] + SUM [X(K,J)*A(J,L)].
C I=1 J=1
C
C Start column loop (index = L).
C L1 (L2): column index of the first (last) row of X(K,L).
C
LNEXT = 1
C
DO 60 L = 1, N
IF( L.LT.LNEXT )
$ GO TO 60
L1 = L
L2 = L
IF( L.LT.N ) THEN
IF( A( L+1, L ).NE.ZERO )
$ L2 = L2 + 1
LNEXT = L2 + 1
END IF
C
C Start row loop (index = K).
C K1 (K2): row index of the first (last) row of X(K,L).
C
KNEXT = L
C
DO 50 K = L, N
IF( K.LT.KNEXT )
$ GO TO 50
K1 = K
K2 = K
IF( K.LT.N ) THEN
IF( A( K+1, K ).NE.ZERO )
$ K2 = K2 + 1
KNEXT = K2 + 1
END IF
C
IF( L1.EQ.L2 .AND. K1.EQ.K2 ) THEN
VEC( 1, 1 ) = C( K1, L1 ) -
$ ( DDOT( K1-1, A( 1, K1 ), 1, C( 1, L1 ), 1 ) +
$ DDOT( L1-1, C( K1, 1 ), LDC, A( 1, L1 ), 1 ) )
SCALOC = ONE
C
A11 = A( K1, K1 ) + A( L1, L1 )
DA11 = ABS( A11 )
IF( DA11.LE.SMIN ) THEN
A11 = SMIN
DA11 = SMIN
INFO = 1
END IF
DB = ABS( VEC( 1, 1 ) )
IF( DA11.LT.ONE .AND. DB.GT.ONE ) THEN
IF( DB.GT.BIGNUM*DA11 )
$ SCALOC = ONE / DB
END IF
X( 1, 1 ) = ( VEC( 1, 1 )*SCALOC ) / A11
C
IF( SCALOC.NE.ONE ) THEN
C
DO 10 J = 1, N
CALL DSCAL( N, SCALOC, C( 1, J ), 1 )
10 CONTINUE
C
SCALE = SCALE*SCALOC
END IF
C( K1, L1 ) = X( 1, 1 )
IF( K1.NE.L1 ) THEN
C( L1, K1 ) = X( 1, 1 )
END IF
C
ELSE IF( L1.EQ.L2 .AND. K1.NE.K2 ) THEN
C
VEC( 1, 1 ) = C( K1, L1 ) -
$ ( DDOT( K1-1, A( 1, K1 ), 1, C( 1, L1 ), 1 ) +
$ DDOT( L1-1, C( K1, 1 ), LDC, A( 1, L1 ), 1 ) )
C
VEC( 2, 1 ) = C( K2, L1 ) -
$ ( DDOT( K1-1, A( 1, K2 ), 1, C( 1, L1 ), 1 ) +
$ DDOT( L1-1, C( K2, 1 ), LDC, A( 1, L1 ), 1 ) )
C
CALL DLALN2( .TRUE., 2, 1, SMIN, ONE, A( K1, K1 ),
$ LDA, ONE, ONE, VEC, 2, -A( L1, L1 ),
$ ZERO, X, 2, SCALOC, XNORM, IERR )
IF( IERR.NE.0 )
$ INFO = 1
C
IF( SCALOC.NE.ONE ) THEN
C
DO 20 J = 1, N
CALL DSCAL( N, SCALOC, C( 1, J ), 1 )
20 CONTINUE
C
SCALE = SCALE*SCALOC
END IF
C( K1, L1 ) = X( 1, 1 )
C( K2, L1 ) = X( 2, 1 )
C( L1, K1 ) = X( 1, 1 )
C( L1, K2 ) = X( 2, 1 )
C
ELSE IF( L1.NE.L2 .AND. K1.EQ.K2 ) THEN
C
VEC( 1, 1 ) = C( K1, L1 ) -
$ ( DDOT( K1-1, A( 1, K1 ), 1, C( 1, L1 ), 1 ) +
$ DDOT( L1-1, C( K1, 1 ), LDC, A( 1, L1 ), 1 ) )
C
VEC( 2, 1 ) = C( K1, L2 ) -
$ ( DDOT( K1-1, A( 1, K1 ), 1, C( 1, L2 ), 1 ) +
$ DDOT( L1-1, C( K1, 1 ), LDC, A( 1, L2 ), 1 ) )
C
CALL DLALN2( .TRUE., 2, 1, SMIN, ONE, A( L1, L1 ),
$ LDA, ONE, ONE, VEC, 2, -A( K1, K1 ),
$ ZERO, X, 2, SCALOC, XNORM, IERR )
IF( IERR.NE.0 )
$ INFO = 1
C
IF( SCALOC.NE.ONE ) THEN
C
DO 30 J = 1, N
CALL DSCAL( N, SCALOC, C( 1, J ), 1 )
30 CONTINUE
C
SCALE = SCALE*SCALOC
END IF
C( K1, L1 ) = X( 1, 1 )
C( K1, L2 ) = X( 2, 1 )
C( L1, K1 ) = X( 1, 1 )
C( L2, K1 ) = X( 2, 1 )
C
ELSE IF( L1.NE.L2 .AND. K1.NE.K2 ) THEN
C
VEC( 1, 1 ) = C( K1, L1 ) -
$ ( DDOT( K1-1, A( 1, K1 ), 1, C( 1, L1 ), 1 ) +
$ DDOT( L1-1, C( K1, 1 ), LDC, A( 1, L1 ), 1 ) )
C
VEC( 1, 2 ) = C( K1, L2 ) -
$ ( DDOT( K1-1, A( 1, K1 ), 1, C( 1, L2 ), 1 ) +
$ DDOT( L1-1, C( K1, 1 ), LDC, A( 1, L2 ), 1 ) )
C
VEC( 2, 1 ) = C( K2, L1 ) -
$ ( DDOT( K1-1, A( 1, K2 ), 1, C( 1, L1 ), 1 ) +
$ DDOT( L1-1, C( K2, 1 ), LDC, A( 1, L1 ), 1 ) )
C
VEC( 2, 2 ) = C( K2, L2 ) -
$ ( DDOT( K1-1, A( 1, K2 ), 1, C( 1, L2 ), 1 ) +
$ DDOT( L1-1, C( K2, 1 ), LDC, A( 1, L2 ), 1 ) )
C
IF( K1.EQ.L1 ) THEN
CALL SB03MW( .FALSE., LUPPER, A( K1, K1 ), LDA,
$ VEC, 2, SCALOC, X, 2, XNORM, IERR )
IF( LUPPER ) THEN
X( 2, 1 ) = X( 1, 2 )
ELSE
X( 1, 2 ) = X( 2, 1 )
END IF
ELSE
CALL DLASY2( .TRUE., .FALSE., 1, 2, 2, A( K1, K1 ),
$ LDA, A( L1, L1 ), LDA, VEC, 2, SCALOC,
$ X, 2, XNORM, IERR )
END IF
IF( IERR.NE.0 )
$ INFO = 1
C
IF( SCALOC.NE.ONE ) THEN
C
DO 40 J = 1, N
CALL DSCAL( N, SCALOC, C( 1, J ), 1 )
40 CONTINUE
C
SCALE = SCALE*SCALOC
END IF
C( K1, L1 ) = X( 1, 1 )
C( K1, L2 ) = X( 1, 2 )
C( K2, L1 ) = X( 2, 1 )
C( K2, L2 ) = X( 2, 2 )
IF( K1.NE.L1 ) THEN
C( L1, K1 ) = X( 1, 1 )
C( L2, K1 ) = X( 1, 2 )
C( L1, K2 ) = X( 2, 1 )
C( L2, K2 ) = X( 2, 2 )
END IF
END IF
C
50 CONTINUE
C
60 CONTINUE
C
ELSE
C
C Solve A*X + X*A' = scale*C.
C
C The (K,L)th block of X is determined starting from
C bottom-right corner column by column by
C
C A(K,K)*X(K,L) + X(K,L)*A(L,L)' = C(K,L) - R(K,L),
C
C where
C N N
C R(K,L) = SUM [A(K,I)*X(I,L)] + SUM [X(K,J)*A(L,J)'].
C I=K+1 J=L+1
C
C Start column loop (index = L).
C L1 (L2): column index of the first (last) row of X(K,L).
C
LNEXT = N
C
DO 120 L = N, 1, -1
IF( L.GT.LNEXT )
$ GO TO 120
L1 = L
L2 = L
IF( L.GT.1 ) THEN
IF( A( L, L-1 ).NE.ZERO )
$ L1 = L1 - 1
LNEXT = L1 - 1
END IF
MINL1N = MIN( L1+1, N )
MINL2N = MIN( L2+1, N )
C
C Start row loop (index = K).
C K1 (K2): row index of the first (last) row of X(K,L).
C
KNEXT = L
C
DO 110 K = L, 1, -1
IF( K.GT.KNEXT )
$ GO TO 110
K1 = K
K2 = K
IF( K.GT.1 ) THEN
IF( A( K, K-1 ).NE.ZERO )
$ K1 = K1 - 1
KNEXT = K1 - 1
END IF
MINK1N = MIN( K1+1, N )
MINK2N = MIN( K2+1, N )
C
IF( L1.EQ.L2 .AND. K1.EQ.K2 ) THEN
VEC( 1, 1 ) = C( K1, L1 ) -
$ ( DDOT( N-K1, A( K1, MINK1N ), LDA,
$ C( MINK1N, L1 ), 1 ) +
$ DDOT( N-L1, C( K1, MINL1N ), LDC,
$ A( L1, MINL1N ), LDA ) )
SCALOC = ONE
C
A11 = A( K1, K1 ) + A( L1, L1 )
DA11 = ABS( A11 )
IF( DA11.LE.SMIN ) THEN
A11 = SMIN
DA11 = SMIN
INFO = 1
END IF
DB = ABS( VEC( 1, 1 ) )
IF( DA11.LT.ONE .AND. DB.GT.ONE ) THEN
IF( DB.GT.BIGNUM*DA11 )
$ SCALOC = ONE / DB
END IF
X( 1, 1 ) = ( VEC( 1, 1 )*SCALOC ) / A11
C
IF( SCALOC.NE.ONE ) THEN
C
DO 70 J = 1, N
CALL DSCAL( N, SCALOC, C( 1, J ), 1 )
70 CONTINUE
C
SCALE = SCALE*SCALOC
END IF
C( K1, L1 ) = X( 1, 1 )
IF( K1.NE.L1 ) THEN
C( L1, K1 ) = X( 1, 1 )
END IF
C
ELSE IF( L1.EQ.L2 .AND. K1.NE.K2 ) THEN
C
VEC( 1, 1 ) = C( K1, L1 ) -
$ ( DDOT( N-K2, A( K1, MINK2N ), LDA,
$ C( MINK2N, L1 ), 1 ) +
$ DDOT( N-L2, C( K1, MINL2N ), LDC,
$ A( L1, MINL2N ), LDA ) )
C
VEC( 2, 1 ) = C( K2, L1 ) -
$ ( DDOT( N-K2, A( K2, MINK2N ), LDA,
$ C( MINK2N, L1 ), 1 ) +
$ DDOT( N-L2, C( K2, MINL2N ), LDC,
$ A( L1, MINL2N ), LDA ) )
C
CALL DLALN2( .FALSE., 2, 1, SMIN, ONE, A( K1, K1 ),
$ LDA, ONE, ONE, VEC, 2, -A( L1, L1 ),
$ ZERO, X, 2, SCALOC, XNORM, IERR )
IF( IERR.NE.0 )
$ INFO = 1
C
IF( SCALOC.NE.ONE ) THEN
C
DO 80 J = 1, N
CALL DSCAL( N, SCALOC, C( 1, J ), 1 )
80 CONTINUE
C
SCALE = SCALE*SCALOC
END IF
C( K1, L1 ) = X( 1, 1 )
C( K2, L1 ) = X( 2, 1 )
C( L1, K1 ) = X( 1, 1 )
C( L1, K2 ) = X( 2, 1 )
C
ELSE IF( L1.NE.L2 .AND. K1.EQ.K2 ) THEN
C
VEC( 1, 1 ) = C( K1, L1 ) -
$ ( DDOT( N-K1, A( K1, MINK1N ), LDA,
$ C( MINK1N, L1 ), 1 ) +
$ DDOT( N-L2, C( K1, MINL2N ), LDC,
$ A( L1, MINL2N ), LDA ) )
C
VEC( 2, 1 ) = C( K1, L2 ) -
$ ( DDOT( N-K1, A( K1, MINK1N ), LDA,
$ C( MINK1N, L2 ), 1 ) +
$ DDOT( N-L2, C( K1, MINL2N ), LDC,
$ A( L2, MINL2N ), LDA ) )
C
CALL DLALN2( .FALSE., 2, 1, SMIN, ONE, A( L1, L1 ),
$ LDA, ONE, ONE, VEC, 2, -A( K1, K1 ),
$ ZERO, X, 2, SCALOC, XNORM, IERR )
IF( IERR.NE.0 )
$ INFO = 1
C
IF( SCALOC.NE.ONE ) THEN
C
DO 90 J = 1, N
CALL DSCAL( N, SCALOC, C( 1, J ), 1 )
90 CONTINUE
C
SCALE = SCALE*SCALOC
END IF
C( K1, L1 ) = X( 1, 1 )
C( K1, L2 ) = X( 2, 1 )
C( L1, K1 ) = X( 1, 1 )
C( L2, K1 ) = X( 2, 1 )
C
ELSE IF( L1.NE.L2 .AND. K1.NE.K2 ) THEN
C
VEC( 1, 1 ) = C( K1, L1 ) -
$ ( DDOT( N-K2, A( K1, MINK2N ), LDA,
$ C( MINK2N, L1 ), 1 ) +
$ DDOT( N-L2, C( K1, MINL2N ), LDC,
$ A( L1, MINL2N ), LDA ) )
C
VEC( 1, 2 ) = C( K1, L2 ) -
$ ( DDOT( N-K2, A( K1, MINK2N ), LDA,
$ C( MINK2N, L2 ), 1 ) +
$ DDOT( N-L2, C( K1, MINL2N ), LDC,
$ A( L2, MINL2N ), LDA ) )
C
VEC( 2, 1 ) = C( K2, L1 ) -
$ ( DDOT( N-K2, A( K2, MINK2N ), LDA,
$ C( MINK2N, L1 ), 1 ) +
$ DDOT( N-L2, C( K2, MINL2N ), LDC,
$ A( L1, MINL2N ), LDA ) )
C
VEC( 2, 2 ) = C( K2, L2 ) -
$ ( DDOT( N-K2, A( K2, MINK2N ), LDA,
$ C( MINK2N, L2 ), 1 ) +
$ DDOT( N-L2, C( K2, MINL2N ), LDC,
$ A( L2, MINL2N ), LDA ) )
C
IF( K1.EQ.L1 ) THEN
CALL SB03MW( .TRUE., LUPPER, A( K1, K1 ), LDA, VEC,
$ 2, SCALOC, X, 2, XNORM, IERR )
IF( LUPPER ) THEN
X( 2, 1 ) = X( 1, 2 )
ELSE
X( 1, 2 ) = X( 2, 1 )
END IF
ELSE
CALL DLASY2( .FALSE., .TRUE., 1, 2, 2, A( K1, K1 ),
$ LDA, A( L1, L1 ), LDA, VEC, 2, SCALOC,
$ X, 2, XNORM, IERR )
END IF
IF( IERR.NE.0 )
$ INFO = 1
C
IF( SCALOC.NE.ONE ) THEN
C
DO 100 J = 1, N
CALL DSCAL( N, SCALOC, C( 1, J ), 1 )
100 CONTINUE
C
SCALE = SCALE*SCALOC
END IF
C( K1, L1 ) = X( 1, 1 )
C( K1, L2 ) = X( 1, 2 )
C( K2, L1 ) = X( 2, 1 )
C( K2, L2 ) = X( 2, 2 )
IF( K1.NE.L1 ) THEN
C( L1, K1 ) = X( 1, 1 )
C( L2, K1 ) = X( 1, 2 )
C( L1, K2 ) = X( 2, 1 )
C( L2, K2 ) = X( 2, 2 )
END IF
END IF
C
110 CONTINUE
C
120 CONTINUE
C
END IF
C
RETURN
C *** Last line of SB03MY ***
END