-
Notifications
You must be signed in to change notification settings - Fork 45
Expand file tree
/
Copy pathmath.py
More file actions
865 lines (730 loc) · 33.3 KB
/
Copy pathmath.py
File metadata and controls
865 lines (730 loc) · 33.3 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
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
#
# math.py
#
# Copyright 2010 Enrico Avventi <avventi@Lonewolf>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
from . import _wrapper
from .exceptions import raise_if_slycot_error
import numpy as np
def mb02ed(typet: str, T: np.ndarray, B: np.ndarray, n: int, k: int, nrhs: int):
""" X, T = mb02ed(typet, T, B, n, k, nrhs)
Solve a system of linear equations T*X = B or X*T = B with a positive
definite block Toeplitz matrix T.
Parameters
----------
typet: str
Specifies the type of T:
- 'R': T contains the first block row of an s.p.d. block Toeplitz matrix,
and the system X*T = B is solved.
- 'C': T contains the first block column of an s.p.d. block Toeplitz matrix,
and the system T*X = B is solved.
Note: the notation x / y means that x corresponds to
typet = 'R' and y corresponds to typet = 'C'.
T : array_like
The leading k-by-n*k / n*k-by-k part of this array must contain the first
block row/column of an s.p.d. block Toeplitz matrix.
B : array_like
The leading nrhs-by-n*k / n*k-by-nrhs part of this array must contain the
right-hand side matrix B.
n : int
The number of blocks in T. n >= 0.
k : int
The number of rows/columns in T, equal to the blocksize. k >= 0.
nrhs : int
The number of right-hand sides. nrhs >= 0.
Returns
-------
X : ndarray
Leading nrhs-by-n*k / n*k-by-nrhs part of
this array contains the solution matrix X.
T: ndarray
If no error is thrown and nrhs > 0, then the leading
k-by-n*k / n*k-by-k part of this array contains the last
row / column of the Cholesky factor of inv(T).
Raises
------
SlycotArithmeticError
:info = 1:
The reduction algorithm failed. The Toeplitz matrix associated
with T is not numerically positive definite.
SlycotParameterError
:info = -1:
typet must be either "R" or "C"
:info = -2:
k must be >= 0
:info = -3:
n must be >= 0
:info = -4:
nrhs must be >= 0
Notes
-----
The algorithm uses Householder transformations, modified hyperbolic rotations,
and block Gaussian eliminations in the Schur algorithm [1], [2].
References
----------
[1] Kailath, T. and Sayed, A.
Fast Reliable Algorithms for Matrices with Structure.
SIAM Publications, Philadelphia, 1999.
[2] Kressner, D. and Van Dooren, P.
Factorizations and linear system solvers for matrices with Toeplitz structure.
SLICOT Working Note 2000-2, 2000.
Numerical Aspects
-----------------
The implemented method is numerically equivalent to forming the Cholesky factor R and the
inverse Cholesky factor of T using the generalized Schur algorithm and solving the systems
of equations R*X = L*B or X*R = B*L by a blocked backward substitution algorithm.
The algorithm requires O(K * N^2 + K * N * NRHS) floating-point operations.
"""
hidden = " (hidden by the wrapper)"
arg_list = [
"typet",
"k",
"n",
"nrhs",
"t",
"ldt" + hidden,
"b",
"ldb" + hidden,
"ldwork" + hidden,
"dwork" + hidden,
"info",
]
T, X, info = _wrapper.mb02ed(typet=typet, k=k, n=n, nrhs=nrhs, t=T, b=B)
raise_if_slycot_error(info, arg_list, docstring=mb02ed.__doc__, checkvars=locals())
return X, T
def mb03rd(n, A, X=None, jobx='U', sort='N', pmax=1.0, tol=0.0):
"""Ar, Xr, blsize, W = mb03rd(n, A, [X, jobx, sort, pmax, tol])
To reduce a matrix `A` in real Schur form to a block-diagonal form
using well-conditioned non-orthogonal similarity transformations.
The condition numbers of the transformations used for reduction
are roughly bounded by `pmax`, where `pmax` is a given value.
The transformations are optionally postmultiplied in a given
matrix `X`. The real Schur form is optionally ordered, so that
clustered eigenvalues are grouped in the same block.
Parameters
----------
n : int
The order of the matrices `A` and `X`. `n` >= 0.
A : (n, n) array_like
The matrix `A` to be block-diagonalized, in real Schur form.
X : (n, n) array_like, optional
A given matrix `X`, for accumulation of transformations (only if
`jobx`='U'). Default value is identity matrix of order `n`.
jobx : {'N', 'U'}, optional
Specifies whether or not the transformations are
accumulated, as follows:
:= 'N': The transformations are not accumulated
:= 'U': The transformations are accumulated in `Xr` (default)
sort : {'N', 'S', 'C', 'B'}, optional
Specifies whether or not the diagonal blocks of the real
Schur form are reordered, as follows:
:= 'N': The diagonal blocks are not reordered (default);
:= 'S': The diagonal blocks are reordered before each
step of reduction, so that clustered eigenvalues
appear in the same block;
:= 'C': The diagonal blocks are not reordered, but the
"closest-neighbour" strategy is used instead of
the standard "closest to the mean" strategy
(see Notes_);
:= 'B': The diagonal blocks are reordered before each
step of reduction, and the "closest-neighbour"
strategy is used (see Notes_).
pmax : float, optional
An upper bound for the infinity norm of elementary
submatrices of the individual transformations used for
reduction (see Notes_). `pmax` >= 1.0
tol : float, optional
The tolerance to be used in the ordering of the diagonal
blocks of the real Schur form matrix.
If the user sets `tol` > 0, then the given value of `tol` is
used as an absolute tolerance: a block `i` and a temporarily
fixed block 1 (the first block of the current trailing
submatrix to be reduced) are considered to belong to the
same cluster if their eigenvalues satisfy
.. math:: | \\lambda_1 - \\lambda_i | <= tol.
If the user sets `tol` < 0, then the given value of tol is
used as a relative tolerance: a block i and a temporarily
fixed block 1 are considered to belong to the same cluster
if their eigenvalues satisfy, for ``j = 1, ..., n``
.. math:: | \\lambda_1 - \\lambda_i | <= | tol | * \\max | \\lambda_j |.
If the user sets `tol` = 0, then an implicitly computed,
default tolerance, defined by ``tol = SQRT( SQRT( EPS ) )``
is used instead, as a relative tolerance, where `EPS` is
the machine precision (see LAPACK Library routine DLAMCH).
If `sort` = 'N' or 'C', this parameter is not referenced.
Returns
-------
Ar : (n, n) ndarray
Contains the computed block-diagonal matrix, in real Schur
canonical form. The non-diagonal blocks are set to zero.
Xr : (n, n) ndarray or None
Contains the product of the given matrix `X` and the
transformation matrix that reduced `A` to block-diagonal
form. The transformation matrix is itself a product of
non-orthogonal similarity transformations having elements
with magnitude less than or equal to `pmax`.
If `jobx` = 'N', this array is returned as None
blsize : (n,) ndarray
The orders of the resulting diagonal blocks of the matrix `Ar`.
W : (n,) complex ndarray
Contains the complex eigenvalues of the matrix `A`.
Notes
-----
**Method**
Consider first that `sort` = 'N'. Let
::
( A A )
( 11 12 )
A = ( ),
( 0 A )
( 22 )
be the given matrix in real Schur form, where initially :math:`A_{11}` is the
first diagonal block of dimension 1-by-1 or 2-by-2. An attempt is
made to compute a transformation matrix `X` of the form
::
( I P )
X = ( ) (1)
( 0 I )
(partitioned as `A`), so that
::
( A 0 )
-1 ( 11 )
X A X = ( ),
( 0 A )
( 22 )
and the elements of `P` do not exceed the value `pmax` in magnitude.
An adaptation of the standard method for solving Sylvester
equations [1]_, which controls the magnitude of the individual
elements of the computed solution [2]_, is used to obtain matrix `P`.
When this attempt failed, an 1-by-1 (or 2-by-2) diagonal block of
:math:`A_{22}` , whose eigenvalue(s) is (are) the closest to the mean of those
of :math:`A_{11}` is selected, and moved by orthogonal similarity
transformations in the leading position of :math:`A_{22}` ; the moved diagonal
block is then added to the block :math:`A_{11}` , increasing its order by 1
(or 2). Another attempt is made to compute a suitable
transformation matrix X with the new definitions of the blocks :math:`A_{11}`
and :math:`A_{22}` . After a successful transformation matrix `X` has been
obtained, it postmultiplies the current transformation matrix
(if `jobx` = 'U'), and the whole procedure is repeated for the
matrix :math:`A_{22}`.
When `sort` = 'S', the diagonal blocks of the real Schur form are
reordered before each step of the reduction, so that each cluster
of eigenvalues, defined as specified in the definition of TOL,
appears in adjacent blocks. The blocks for each cluster are merged
together, and the procedure described above is applied to the
larger blocks. Using the option `sort` = 'S' will usually provide
better efficiency than the standard option (`sort` = 'N'), proposed
in [2]_, because there could be no or few unsuccessful attempts
to compute individual transformation matrices `X` of the form (1).
However, the resulting dimensions of the blocks are usually
larger; this could make subsequent calculations less efficient.
When `sort` = 'C' or 'B', the procedure is similar to that for
`sort` = 'N' or 'S', respectively, but the block of :math:`A_{22}` whose
eigenvalue(s) is (are) the closest to those of :math:`A_{11}` (not to their
mean) is selected and moved to the leading position of :math:`A_{22}`. This
is called the "closest-neighbour" strategy.
**Numerical Aspects**
The algorithm usually requires :math:`\\mathcal{O}(N^3)` operations,
but :math:`\\mathcal{O}(N^4)` are
possible in the worst case, when all diagonal blocks in the real
Schur form of `A` are 1-by-1, and the matrix cannot be diagonalized
by well-conditioned transformations.
**Further Comments**
The individual non-orthogonal transformation matrices used in the
reduction of `A` to a block-diagonal form have condition numbers
of the order `pmax`*`pmax`. This does not guarantee that their product
is well-conditioned enough. The routine can be easily modified to
provide estimates for the condition numbers of the clusters of
eigenvalues.
**Contributor**
V. Sima, Katholieke Univ. Leuven, Belgium, June 1998.
Partly based on the RASP routine BDIAG by A. Varga, German
Aerospace Center, DLR Oberpfaffenhofen.
**Revisions**
\\V. Sima, Research Institute for Informatics, Bucharest, Apr. 2003.
References
----------
.. [1] Bartels, R.H. and Stewart, G.W. T
Solution of the matrix equation A X + XB = C.
Comm. A.C.M., 15, pp. 820-826, 1972.
.. [2] Bavely, C. and Stewart, G.W.
An Algorithm for Computing Reducing Subspaces by Block
Diagonalization.
SIAM J. Numer. Anal., 16, pp. 359-367, 1979.
.. [3] Demmel, J.
The Condition Number of Equivalence Transformations that
Block Diagonalize Matrix Pencils.
SIAM J. Numer. Anal., 20, pp. 599-610, 1983.
"""
hidden = ' (hidden by the wrapper)'
arg_list = ['jobx', 'sort', 'n', 'pmax',
'A', 'lda' + hidden, 'X', 'ldx' + hidden,
'nblcks', 'blsize', 'wr', 'wi', 'tol',
'dwork' + hidden, 'info']
if X is None:
X = np.eye(n)
Ar, Xr, nblcks, blsize, wr, wi, info = _wrapper.mb03rd(
jobx, sort, n, pmax, A, X, tol)
raise_if_slycot_error(info, arg_list)
if jobx == 'N':
Xr = None
else:
Xr = Xr[:n, :n]
Ar = Ar[:n, :n]
W = wr + 1J*wi
return Ar, Xr, blsize[:nblcks], W
def mb03vd(n, ilo, ihi, A):
""" HQ, Tau = mb03vd(n, ilo, ihi, A)
To reduce a product of p real general matrices A = A_1*A_2*...*A_p
to upper Hessenberg form, H = H_1*H_2*...*H_p, where H_1 is
upper Hessenberg, and H_2, ..., H_p are upper triangular, by using
orthogonal similarity transformations on A,
Q_1' * A_1 * Q_2 = H_1,
Q_2' * A_2 * Q_3 = H_2,
...
Q_p' * A_p * Q_1 = H_p.
Parameters
----------
n : int
The order of the square matrices A_1, A_2, ..., A_p.
n >= 0.
ilo, ihi : int
It is assumed that all matrices A_j, j = 2, ..., p, are
already upper triangular in rows and columns [:ilo-1] and
[ihi:n], and A_1 is upper Hessenberg in rows and columns
[:ilo-1] and [ihi:n], with A_1[ilo-1,ilo-2] = 0 (unless
ilo = 1), and A_1[ihi,ihi-1] = 0 (unless ihi = n).
If this is not the case, ilo and ihi should be set to 1
and n, respectively.
1 <= ilo <= max(1,n); min(ilo,n) <= ihi <= n.
A : ndarray
A[:n,:n,:p] must contain the matrices of factors to be reduced;
specifically, A[:,:,j-1] must contain A_j, j = 1, ..., p.
Returns
-------
HQ : ndarray
3D array with same shape as A. The upper triangle and the first
subdiagonal of HQ[:n,:n,0] contain the upper Hessenberg
matrix H_1, and the elements below the first subdiagonal,
with the first column of the array Tau represent the
orthogonal matrix Q_1 as a product of elementary
reflectors. See FURTHER COMMENTS.
For j > 1, the upper triangle of HQ[:n,:n,j-1]
contains the upper triangular matrix H_j, and the elements
below the diagonal, with the j-th column of the array TAU
represent the orthogonal matrix Q_j as a product of
elementary reflectors. See FURTHER COMMENTS.
Tau : ndarray
2D array with shape (max(1, n-1), p).
The leading n-1 elements in the j-th column contain the
scalar factors of the elementary reflectors used to form
the matrix Q_j, j = 1, ..., p. See FURTHER COMMENTS.
Notes
-----
Each matrix Q_j is represented as a product of (ihi-ilo)
elementary reflectors,
Q_j = H_j(ilo) H_j(ilo+1) . . . H_j(ihi-1).
Each H_j(i), i = ilo, ..., ihi-1, has the form
H_j(i) = I - tau_j * v_j * v_j',
where tau_j is a real scalar, and v_j is a real vector with
v_j(1:i) = 0, v_j(i+1) = 1 and v_j(ihi+1:n) = 0; v_j(i+2:ihi)
is stored on exit in A_j(i+2:ihi,i), and tau_j in TAU(i,j).
The contents of A_1 are illustrated by the following example
for n = 7, ilo = 2, and ihi = 6:
on entry on exit
( a a a a a a a ) ( a h h h h h a )
( 0 a a a a a a ) ( 0 h h h h h a )
( 0 a a a a a a ) ( 0 h h h h h h )
( 0 a a a a a a ) ( 0 v2 h h h h h )
( 0 a a a a a a ) ( 0 v2 v3 h h h h )
( 0 a a a a a a ) ( 0 v2 v3 v4 h h h )
( 0 0 0 0 0 0 a ) ( 0 0 0 0 0 0 a )
where a denotes an element of the original matrix A_1, h denotes
a modified element of the upper Hessenberg matrix H_1, and vi
denotes an element of the vector defining H_1(i).
The contents of A_j, j > 1, are illustrated by the following
example for n = 7, ilo = 2, and ihi = 6:
on entry on exit
( a a a a a a a ) ( a h h h h h a )
( 0 a a a a a a ) ( 0 h h h h h h )
( 0 a a a a a a ) ( 0 v2 h h h h h )
( 0 a a a a a a ) ( 0 v2 v3 h h h h )
( 0 a a a a a a ) ( 0 v2 v3 v4 h h h )
( 0 a a a a a a ) ( 0 v2 v3 v4 v5 h h )
( 0 0 0 0 0 0 a ) ( 0 0 0 0 0 0 a )
where a denotes an element of the original matrix A_j, h denotes
a modified element of the upper triangular matrix H_j, and vi
denotes an element of the vector defining H_j(i). (The element
(1,2) in A_p is also unchanged for this example.)
"""
hidden = ' (hidden by the wrapper)'
arg_list = ['n', 'p' + hidden,
'ilo', 'ihi', 'a',
'lda1' + hidden, 'lda2' + hidden, 'tau',
'ldtau' + hidden, 'dwork' + hidden, 'info']
HQ, Tau, info = _wrapper.mb03vd(n, ilo, ihi, A)
raise_if_slycot_error(info, arg_list)
return (HQ, Tau)
def mb03vy(n, ilo, ihi, A, Tau, ldwork=None):
""" Q = mb03vy(n, ilo, ihi, A, Tau, [ldwork])
To generate the real orthogonal matrices Q_1, Q_2, ..., Q_p,
which are defined as the product of ihi-ilo elementary reflectors
of order n, as returned by SLICOT Library routine MB03VD:
Q_j = H_j(ilo) H_j(ilo+1) . . . H_j(ihi-1).
Parameters
----------
n : int
The order of the matrices Q_1, Q_2, ..., Q_p. N >= 0.
ilo, ihi : int
The values of the indices ilo and ihi, respectively, used
in the previous call of the SLICOT Library routine MB03VD.
1 <= ilo <= max(1,n); min(ilo,n) <= ihi <= n.
A : ndarray
A[:n,:n,j-1] must contain the vectors which define the
elementary reflectors used for reducing A_j, as returned
by SLICOT Library routine MB03VD, j = 1, ..., p.
Tau : ndarray
The leading N-1 elements in the j-th column must contain
the scalar factors of the elementary reflectors used to
form the matrix Q_j, as returned by SLICOT Library routine
MB03VD.
ldwork : int, optional
The length of the internal array DWORK. ldwork >= max(1, n).
For optimum performance ldwork should be larger.
Returns
-------
Q : ndarray
3D array with same shape as A. Q[:n,:n,j-1] contains the
N-by-N orthogonal matrix Q_j, j = 1, ..., p.
"""
hidden = ' (hidden by the wrapper)'
arg_list = ['n', 'p' + hidden,
'ilo', 'ihi', 'a',
'lda1' + hidden, 'lda2' + hidden, 'tau',
'ldtau' + hidden, 'dwork' + hidden, 'ldwork', 'info' + hidden]
if not ldwork:
ldwork = max(1, 2 * n)
Q, info = _wrapper.mb03vy(n, ilo, ihi, A, Tau, ldwork)
raise_if_slycot_error(info, arg_list)
return Q
def mb03wd(job, compz, n, ilo, ihi, iloz, ihiz, H, Q, ldwork=None):
""" T, Z, Wr = mb03wd(job, compz, n, ilo, ihi, iloz, ihiz, H, Q, [ldwork])
To compute the Schur decomposition and the eigenvalues of a
product of matrices, H = H_1*H_2*...*H_p, with H_1 an upper
Hessenberg matrix and H_2, ..., H_p upper triangular matrices,
without evaluating the product. Specifically, the matrices Z_i
are computed, such that
::
Z_1' * H_1 * Z_2 = T_1,
Z_2' * H_2 * Z_3 = T_2,
...
Z_p' * H_p * Z_1 = T_p,
where T_1 is in real Schur form, and T_2, ..., T_p are upper
triangular.
The routine works primarily with the Hessenberg and triangular
submatrices in rows and columns ilo to ihi, but optionally applies
the transformations to all the rows and columns of the matrices
H_i, i = 1,...,p. The transformations can be optionally
accumulated.
Parameters
----------
job : {'E', 'S'}
Indicates whether the user wishes to compute the full
Schur form or the eigenvalues only, as follows:
= 'E': Compute the eigenvalues only;
= 'S': Compute the factors T_1, ..., T_p of the full
Schur form, T = T_1*T_2*...*T_p.
compz : {'N', 'I', 'V'}
Indicates whether or not the user wishes to accumulate
the matrices Z_1, ..., Z_p, as follows:
= 'N': The matrices Z_1, ..., Z_p are not required;
= 'I': Z_i is initialized to the unit matrix and the
orthogonal transformation matrix Z_i is returned,
i = 1, ..., p;
= 'V': Z_i must contain an orthogonal matrix Q_i on
entry, and the product Q_i*Z_i is returned,
i = 1, ..., p.
n : int
The order of the matrix H. n >= 0
ilo, ihi : int
It is assumed that all matrices H_j, j = 2, ..., p, are
already upper triangular in rows and columns [:ilo-1] and
[ihi:n], and H_1 is upper quasi-triangular in rows and
columns [:ilo-1] and [ihi:n], with H_1[ilo-1,ilo-2] = 0
(unless ilo = 1), and H_1[ihi,ihi-1] = 0 (unless ihi = n).
The routine works primarily with the Hessenberg submatrix
in rows and columns ilo to ihi, but applies the
transformations to all the rows and columns of the
matrices H_i, i = 1,...,p, if JOB = 'S'.
1 <= ilo <= max(1,n); min(ilo,n) <= ihi <= n.
iloz, ihiz : int
Specify the rows of Z to which the transformations must be
applied if compz = 'I' or compz = 'V'.
1 <= iloz <= ilo; ihi <= ihiz <= n.
H : array_like
H[:n,:n,0] must contain the upper Hessenberg matrix H_1 and
H[:n,:n,j-1] for j > 1 must contain the upper triangular matrix
H_j, j = 2, ..., p.
Q : array_like
If compz = 'V', Q[:n,:n,:p] must contain the current matrix Q of
transformations accumulated by SLICOT Library routine
MB03VY.
If compz = 'I', Q is ignored
ldwork : int, optional
The length of the cache array. The default value is
ihi-ilo+p-1
Returns
-------
T : ndarray
3D array with the same shape as H.
If JOB = 'S', T[:n,:n,0] is upper quasi-triangular in rows
and columns [ilo-1:ihi], with any 2-by-2 diagonal blocks
corresponding to a pair of complex conjugated eigenvalues, and
T[:n,:n,j-1] for j > 1 contains the resulting upper
triangular matrix T_j.
If job = 'E', T is None
Z : ndarray
3D array with the same shape as Q.
If compz = 'V', or compz = 'I', the leading
N-by-N-by-P part of this array contains the transformation
matrices which produced the Schur form; the
transformations are applied only to the submatrices
Z[iloz-1:ihiz,ilo-1:ihi,j-1], j = 1, ..., p.
If compz = 'N', Z is None
W : (n,) complex ndarray
The computed eigenvalues ilo to ihi. If two eigenvalues
are computed as a complex conjugate pair, they are stored
in consecutive elements of W say the i-th and
(i+1)th, with imag(W][i]) > 0 and imag(W[i+1]) < 0.
If JOB = 'S', the eigenvalues are stored in the same order
as on the diagonal of the Schur form returned in H.
Warns
-----
SlycotResultWarning
:info > 0:
failed to compute all the eigenvalues {ilo} to {ihi}
in a total of 30*({ihi}-{ilo}+1) iterations
the elements Wr[{info}:{ihi}] contains those
eigenvalues which have been successfully computed.
"""
hidden = ' (hidden by the wrapper)'
arg_list = ['job', 'compz', 'n', 'p' + hidden,
'ilo', 'ihi', 'iloz', 'ihiz',
'h', 'ldh1' + hidden, 'ldh2' + hidden,
'z', 'ldz1' + hidden, 'ldz2' + hidden,
'wr', 'wi',
'dwork' + hidden, 'ldwork', 'info' + hidden]
if not ldwork:
p = H.shape[2]
ldwork = max(1, ihi - ilo + p - 1)
T, Z, Wr, Wi, info = _wrapper.mb03wd(
job, compz, n, ilo, ihi, iloz, ihiz, H, Q, ldwork)
raise_if_slycot_error(info, arg_list, mb03rd.__doc__, locals())
if job == 'E':
T = None
if compz == 'N':
Z = None
W = Wr + Wi*1J
return (T, Z, W)
def mb05md(a, delta, balanc='N'):
"""Ar, Vr, Yr, w = mb05md(a, delta, balanc='N')
Matrix exponential for a real non-defective matrix
To compute ``exp(A*delta)`` where `A` is a real N-by-N non-defective
matrix with real or complex eigenvalues and delta is a scalar
value. The routine also returns the eigenvalues and eigenvectors
of `A` as well as (if all eigenvalues are real) the matrix product
``exp(Lambda*delta)`` times the inverse of the eigenvector matrix of
`A`, where `Lambda` is the diagonal matrix of eigenvalues.
Optionally, the routine computes a balancing transformation to
improve the conditioning of the eigenvalues and eigenvectors.
Parameters
----------
A : (n, n) array_like
Square matrix
delta : float
The scalar value delta of the problem.
balanc : {'N', 'S'}, optional
Indicates how the input matrix should be diagonally scaled
to improve the conditioning of its eigenvalues as follows:
:= 'N': Do not diagonally scale;
:= 'S': Diagonally scale the matrix, i.e. replace `A` by
``D*A*D**(-1)``, where `D` is a diagonal matrix chosen
to make the rows and columns of A more equal in
norm. Do not permute.
Returns
-------
Ar : (n, n) ndarray
Contains the solution matrix ``exp(A*delta)``
Vr : (n, n) ndarray
Contains the eigenvector matrix for `A`. If the `k`-th
eigenvalue is real the `k`-th column of the eigenvector
matrix holds the eigenvector corresponding to the `k`-th
eigenvalue. Otherwise, the `k`-th and `(k+1)`-th eigenvalues
form a complex conjugate pair and the k-th and `(k+1)`-th
columns of the eigenvector matrix hold the real and
imaginary parts of the eigenvectors corresponding to these
eigenvalues as follows. If `p` and `q` denote the `k`-th and
`(k+1)`-th columns of the eigenvector matrix, respectively,
then the eigenvector corresponding to the complex
eigenvalue with positive (negative) imaginary value is
given by
``p + q*j (p - q*j), where j^2 = -1.``
Yr : (n, n) ndarray
contains an intermediate result for computing the matrix
exponential. Specifically, ``exp(A*delta)`` is obtained as the
product ``V*Y``, where `V` is the matrix stored in the leading
`n`-by-`n` part of the array `V`. If all eigenvalues of `A` are
real, then the leading `n`-by-`n` part of this array contains
the matrix product ``exp(Lambda*delta)`` times the inverse of
the (right) eigenvector matrix of `A`, where `Lambda` is the
diagonal matrix of eigenvalues.
w : (n, ) real or complex ndarray
Contains the eigenvalues of the matrix `A`. The eigenvalues
are unordered except that complex conjugate pairs of values
appear consecutively with the eigenvalue having positive
imaginary part first.
Warns
------
SlycotResultWarning
:0 < info <=n:
the QR algorithm failed to compute all
the eigenvalues; no eigenvectors have been computed;
w[{info}:{n}] contains eigenvalues which have converged;
:info == n+1:
The inverse of the eigenvector matrix could not
be formed due to an attempt to divide by zero, i.e.,
the eigenvector matrix is singular;
:info == n+2:
Matrix A is defective, possibly due to rounding errors.
"""
hidden = ' (hidden by the wrapper)'
arg_list = ['balanc', 'n', 'delta', 'a', 'lda'+hidden, 'v', 'ldv'+hidden,
'y', 'ldy'+hidden, 'valr', 'vali',
'iwork'+hidden, 'dwork'+hidden, 'ldwork'+hidden,
'info'+hidden]
n = min(a.shape)
(Ar, Vr, Yr, VALr, VALi, INFO) = _wrapper.mb05md(balanc=balanc,
n=n,
delta=delta,
a=a)
raise_if_slycot_error(INFO, arg_list, mb05md.__doc__, locals())
if not all(VALi == 0):
w = VALr + 1J*VALi
else:
w = VALr
return (Ar, Vr, Yr, w)
def mb05nd(a, delta, tol=1e-7):
"""F, H = mb05nd(n, a, delta, tol=1e-7)
To compute
::
(a) F(delta) = exp(A*delta) and
(b) H(delta) = Int[F(s) ds] from s = 0 to s = delta,
where `A` is a real`n`-by-`n` matrix and `delta` is a scalar value.
Parameters
----------
A : (n, n) array_like
Square matrix
delta : float
The scalar value delta of the problem.
tol : float, optional
Tolerance. A good value is sqrt(eps).
Default is 1e-7.
Returns
-------
F : (n, n) ndarray
exp(A*delta)
H : (n, n) ndarray
Int[F(s) ds] from s = 0 to s = delta,
Raises
------
SlycotArithmeticError
:1 < info <=n:
the ({info},{info}) element of the denominator of
the Pade approximation is zero, so the denominator
is exactly singular;
:info == n+1:
``DELTA = (delta * frobenius norm of matrix A)`` is
probably too large to permit meaningful computation.
That is, {delta} > SQRT(BIG), where BIG is a
representable number near the overflow threshold of
the machine (see LAPACK Library Routine DLAMCH).
"""
hidden = ' (hidden by the wrapper)'
arg_list = ['n', 'delta', 'a', 'lda'+hidden, 'ex', 'ldex'+hidden,
'exint', 'ldexin'+hidden, 'tol', 'iwork'+hidden,
'dwork'+hidden, 'ldwork'+hidden]
n = min(a.shape)
out = _wrapper.mb05nd(n=n, delta=delta, a=a, tol=tol)
raise_if_slycot_error(out[-1], arg_list, mb05nd.__doc__, locals())
return out[:-1]
def mc01td(dico, dp, p):
"""dp, stable, nz = mc01td(dico, dp, p)
To determine whether or not a given polynomial P(x) with real
coefficients is stable, either in the continuous-time or discrete-
time case.
A polynomial is said to be stable in the continuous-time case
if all its zeros lie in the left half-plane, and stable in the
discrete-time case if all its zeros lie inside the unit circle.
Parameters
----------
dico : {'C', 'D'}
Indicates whether the stability test to be applied to `P(x)` is in
the continuous-time or discrete-time case as follows::
= 'C': continuous-time case;
= 'D': discrete-time case.
dp : int
The degree of the polynomial `P(x)`. ``dp >= 0``.
p : (dp+1, ) array_like
This array must contain the coefficients of `P(x)` in increasing
powers of `x`.
Returns
-------
dp : int
If ``P(dp+1) = 0.0`` on entry, then `dp` contains the index of the
highest power of `x` for which ``P(dp+1) <> 0.0``.
stable : int
Equal to 1 if `P(x)` is stable, 0 otherwise.
nz : int
The number of unstable zeros.
Warns
-----
SlycotResultWarning
:info == 1:
Entry ``P(x)`` is the zero polynomial.
:info == 2 and dico == 'C':
The polynomial ``P(x)`` is most probably unstable,
although it may be stable with one or more zeros
very close to the imaginary axis.
The number of unstable zeros (NZ) is not determined.
:info == 2 and dico == 'D':
The polynomial ``P(x)`` is most probably unstable,
although it may be stable with one or more zeros
very close to the the unit circle.
The number of unstable zeros (NZ) is not determined.
:iwarn > 0:
The degree of the polynomial ``P(x)`` has been
reduced to ``(DB - {iwarn})`` because
``P(DB+1-j) = 0.0`` on entry
for ``j = 0, 1,..., k-1`` and ``P(DB+1-k) <> 0.0``.
"""
hidden = ' (hidden by the wrapper)'
arg_list = ['dico', 'dp', 'P', 'stable', 'nz', 'DWORK' + hidden,
'IWARN', 'INFO']
(dp_out, stable_log, nz, iwarn, info) = _wrapper.mc01td(dico, dp, p)
raise_if_slycot_error([iwarn, info], arg_list, mc01td.__doc__, locals())
ftrue, ffalse = _wrapper.ftruefalse()
stable = 1 if stable_log == ftrue else 0
return (dp_out, stable, nz)