Skip to content

Commit 009a2b8

Browse files
committed
adding the mb03rd wrapper
1 parent b89132c commit 009a2b8

File tree

3 files changed

+206
-0
lines changed

3 files changed

+206
-0
lines changed

slycot/src/transform.pyf

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,3 +528,40 @@ subroutine tg01fd_uu(compq,compz,joba,l,n,m,p,a,lda,e,lde,b,ldb,c,ldc,q,ldq,z,ld
528528
integer required intent(in) :: ldwork
529529
integer intent(out) :: info
530530
end subroutine tg01fd_uu
531+
subroutine mb03rd_n(jobx,sort,n,pmax,a,lda,x,ldx,nblcks,blsize,wr,wi,tol,dwork,info) ! in MB03RD.f
532+
fortranname mb03rd
533+
character intent(hide) :: jobx = 'N'
534+
character intent(in),required :: sort
535+
integer intent(in),required,check(n>0) :: n
536+
double precision intent(in),required,check(pmax>=1.0) :: pmax
537+
double precision intent(in,out,copy),dimension(n,n),depend(n) :: a
538+
integer intent(hide),depend(a) :: lda=MAX(shape(a,0),1)
539+
double precision intent(cache,hide) :: x
540+
integer intent(in,hide) :: ldx=1
541+
integer intent(out) :: nblcks
542+
integer intent(out),dimension(n),depend(n) :: blsize
543+
double precision intent(out),dimension(n),depend(n) :: wr
544+
double precision intent(out),dimension(n),depend(n) :: wi
545+
double precision intent(in) :: tol
546+
double precision intent(cache,hide),dimension(n),depend(n) :: dwork
547+
integer intent(out) :: info
548+
end subroutine mb03rd_n
549+
subroutine mb03rd_u(jobx,sort,n,pmax,a,lda,x,ldx,nblcks,blsize,wr,wi,tol,dwork,info) ! in MB03RD.f
550+
fortranname mb03rd
551+
character intent(hide) :: jobx = 'U'
552+
character intent(in),required :: sort
553+
integer intent(in),required,check(n>0) :: n
554+
double precision intent(in),required,check(pmax>=1.0) :: pmax
555+
double precision intent(in,out,copy),dimension(n,n),depend(n) :: a
556+
integer intent(hide),depend(a) :: lda=MAX(shape(a,0),1)
557+
double precision intent(in,out,copy),dimension(n,n),depend(n) :: x
558+
integer intent(hide),depend(x) :: ldx=MAX(shape(x,0),1)
559+
integer intent(out) :: nblcks
560+
integer intent(out),dimension(n),depend(n) :: blsize
561+
double precision intent(out),dimension(n),depend(n) :: wr
562+
double precision intent(out),dimension(n),depend(n) :: wi
563+
double precision intent(in) :: tol
564+
double precision intent(cache,hide),dimension(n),depend(n) :: dwork
565+
integer intent(out) :: info
566+
end subroutine mb03rd_u
567+

slycot/tests/test_mb03rd.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env python
2+
#
3+
# test_mb03rd.py - test suite for Shur form reduction
4+
# RvP, 31 Jul 2019
5+
import unittest
6+
from slycot import transform
7+
import numpy as np
8+
from numpy.testing import assert_raises, assert_almost_equal, assert_equal
9+
from scipy.linalg import schur
10+
11+
test1_A = np.array([
12+
[ 1., -1., 1., 2., 3., 1., 2., 3.],
13+
[ 1., 1., 3., 4., 2., 3., 4., 2.],
14+
[ 0., 0., 1., -1., 1., 5., 4., 1.],
15+
[ 0., 0., 0., 1., -1., 3., 1., 2.],
16+
[ 0., 0., 0., 1., 1., 2., 3., -1.],
17+
[ 0., 0., 0., 0., 0., 1., 5., 1.],
18+
[ 0., 0., 0., 0., 0., 0., 0.99999999, -0.99999999 ],
19+
[ 0., 0., 0., 0., 0., 0., 0.99999999, 0.99999999 ]
20+
])
21+
test1_n = test1_A.shape[0]
22+
23+
test1_Ar = np.array([
24+
[ 1.0000, -1.0000, -1.2247, -0.7071, -3.4186, 1.4577, 0.0000, 0.0000 ],
25+
[ 1.0000, 1.0000, 0.0000, 1.4142, -5.1390, 3.1637, 0.0000, 0.0000 ],
26+
[ 0.0000, 0.0000, 1.0000, -1.7321, -0.0016, 2.0701, 0.0000, 0.0000 ],
27+
[ 0.0000, 0.0000, 0.5774, 1.0000, 0.7516, 1.1379, 0.0000, 0.0000 ],
28+
[ 0.0000, 0.0000, 0.0000, 0.0000, 1.0000, -5.8606, 0.0000, 0.0000 ],
29+
[ 0.0000, 0.0000, 0.0000, 0.0000, 0.1706, 1.0000, 0.0000, 0.0000 ],
30+
[ 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 1.0000, -0.8850 ],
31+
[ 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 1.0000 ],
32+
])
33+
34+
test1_Xr = np.array([
35+
[ 1.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.9045, 0.1957 ],
36+
[ 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 0.0000, -0.3015, 0.9755 ],
37+
[ 0.0000, 0.0000, 0.8165, 0.0000, -0.5768, -0.0156, -0.3015, 0.0148 ],
38+
[ 0.0000, 0.0000, -0.4082, 0.7071, -0.5768, -0.0156, 0.0000, -0.0534 ],
39+
[ 0.0000, 0.0000, -0.4082, -0.7071, -0.5768, -0.0156, 0.0000, 0.0801 ],
40+
[ 0.0000, 0.0000, 0.0000, 0.0000, -0.0276, 0.9805, 0.0000, 0.0267 ],
41+
[ 0.0000, 0.0000, 0.0000, 0.0000, 0.0332, -0.0066, 0.0000, 0.0000 ],
42+
[ 0.0000, 0.0000, 0.0000, 0.0000, 0.0011, 0.1948, 0.0000, 0.0000 ]
43+
])
44+
45+
test1_pmax = 1e3
46+
test1_tol = 0.01
47+
class test_mb03rd(unittest.TestCase):
48+
def test1(self):
49+
# create schur form with scipy
50+
A, X = schur(test1_A)
51+
Ah, Xh = np.copy(A), np.copy(X)
52+
# on this basis, get the transform
53+
Ar, Xr, blks, eig = transform.mb03rd(
54+
test1_n, A, X, 'U', 'S', test1_pmax, test1_tol)
55+
# ensure X and A are unchanged
56+
assert_equal(A, Ah)
57+
assert_equal(X, Xh)
58+
# compare to test case results
59+
assert_almost_equal(Ar, test1_Ar, decimal=4)
60+
assert_almost_equal(Xr, test1_Xr, decimal=4)
61+
62+
if __name__ == "__main__":
63+
unittest.main()

slycot/transform.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,4 +1353,110 @@ def tg01fd(l,n,m,p,A,E,B,C,Q=None,Z=None,compq='N',compz='N',joba='N',tol=0.0,ld
13531353

13541354
return A,E,B,C,ranke,rnka22,Q,Z
13551355

1356+
def mb03rd(n,A,X=None,jobx='U',sort='N',pmax=1.0,tol=0.0):
1357+
""" A,X,blcks,EIG = mb03rd(n,A,[X,job,sort,pmax,tol]) -- if jobx='U'
1358+
A,blcks,EIG = mb03rd(n,A,[X,job,sort,pmax,tol]) -- if jobx='N'
1359+
1360+
To reduce a matrix A in real Schur form to a block-diagonal form
1361+
using well-conditioned non-orthogonal similarity transformations.
1362+
The condition numbers of the transformations used for reduction
1363+
are roughly bounded by pmax*pmax, where pmax is a given value.
1364+
The transformations are optionally postmultiplied in a given
1365+
matrix X. The real Schur form is optionally ordered, so that
1366+
clustered eigenvalues are grouped in the same block.
1367+
1368+
Required arguments:
1369+
n : input int
1370+
The order of the matrices A and X. n >= 0.
1371+
A : input rank-2 array('d') with bounds (n,n)
1372+
the matrix A to be block-diagonalized, in real Schur form.
1373+
Optional arguments:
1374+
X : input rank-2 array('d') with bounds (n,n)
1375+
a given matrix X, for accumulation of transformations (only if
1376+
jobx='U'
1377+
jobx : input char*1
1378+
Specifies whether or not the transformations are
1379+
accumulated, as follows:
1380+
= 'N': The transformations are not accumulated;
1381+
= 'U': The transformations are accumulated in X (the
1382+
given matrix X is updated).
1383+
sort : input char*1
1384+
Specifies whether or not the diagonal blocks of the real
1385+
Schur form are reordered, as follows:
1386+
= 'N': The diagonal blocks are not reordered;
1387+
= 'S': The diagonal blocks are reordered before each
1388+
step of reduction, so that clustered eigenvalues
1389+
appear in the same block;
1390+
= 'C': The diagonal blocks are not reordered, but the
1391+
"closest-neighbour" strategy is used instead of
1392+
the standard "closest to the mean" strategy
1393+
(see METHOD);
1394+
= 'B': The diagonal blocks are reordered before each
1395+
step of reduction, and the "closest-neighbour"
1396+
strategy is used (see METHOD).
1397+
pmax : input float
1398+
An upper bound for the infinity norm of elementary
1399+
submatrices of the individual transformations used for
1400+
reduction (see METHOD). PMAX >= 1.0D0.
1401+
tol : input float
1402+
The tolerance to be used in the ordering of the diagonal
1403+
blocks of the real Schur form matrix.
1404+
If the user sets TOL > 0, then the given value of TOL is
1405+
used as an absolute tolerance: a block i and a temporarily
1406+
fixed block 1 (the first block of the current trailing
1407+
submatrix to be reduced) are considered to belong to the
1408+
same cluster if their eigenvalues satisfy
1409+
1410+
| lambda_1 - lambda_i | <= TOL.
1411+
1412+
If the user sets TOL < 0, then the given value of TOL is
1413+
used as a relative tolerance: a block i and a temporarily
1414+
fixed block 1 are considered to belong to the same cluster
1415+
if their eigenvalues satisfy, for j = 1, ..., N,
1416+
1417+
| lambda_1 - lambda_i | <= | TOL | * max | lambda_j |.
1418+
1419+
If the user sets TOL = 0, then an implicitly computed,
1420+
default tolerance, defined by TOL = SQRT( SQRT( EPS ) )
1421+
is used instead, as a relative tolerance, where EPS is
1422+
the machine precision (see LAPACK Library routine DLAMCH).
1423+
If SORT = 'N' or 'C', this parameter is not referenced.
1424+
Return objects:
1425+
Ar : output rank-2 array('d') with bounds (n,n)
1426+
Contains the computed block-diagonal matrix, in real Schur
1427+
canonical form. The non-diagonal blocks are set to zero.
1428+
Xr : output rank-2 array('d') with bounds (n,n)
1429+
Contains the product of the given matrix X and the
1430+
transformation matrix that reduced A to block-diagonal
1431+
form. The transformation matrix is itself a product of
1432+
non-orthogonal similarity transformations having elements
1433+
with magnitude less than or equal to PMAX.
1434+
If JOBX = 'N', this array is not referenced, and not returned
1435+
blksize : output rank-1 array('i') with bounds (n)
1436+
The orders of the resulting diagonal blocks of the matrix Ar.
1437+
W : output rank-1 array('c') size (n)
1438+
This arrays contain the eigenvalues of the matrix A.
1439+
"""
1440+
hidden = ' (hidden by the wrapper)'
1441+
arg_list = ('jobx', 'sort', 'n', 'pmax', 'A', 'LDA'+hidden,
1442+
'X', 'LDX'+hidden, 'nblks'+hidden, 'blsize'+hidden,
1443+
'WR'+hidden, 'WI'+hidden, 'tol',
1444+
'DWORK'+hidden, 'INFO'+hidden)
1445+
if jobx == 'N':
1446+
out = _wrapper.mb03rd_n(sort, n, pmax, A, tol)
1447+
else:
1448+
if X is None:
1449+
X = _np.eye(n)
1450+
out = _wrapper.mb03rd_u(sort, n, pmax, A, X, tol)
1451+
1452+
if out[-1] < 0:
1453+
error_text = "The following argument had an illegal value: "+arg_list[-out[-1]-1]
1454+
e = ValueError(error_text)
1455+
e.info = out[-1]
1456+
raise e
1457+
if jobx == 'N':
1458+
return out[0], out[2][:out[1]], out[-3] + out[-2]*1j
1459+
else:
1460+
return out[0], out[1], out[3][:out[2]], out[-3] + out[-2]*1j
1461+
13561462
# to be replaced by python wrappers

0 commit comments

Comments
 (0)