Skip to content

Commit ef138a6

Browse files
kwlee2025cppclaude
andcommitted
Correct SG03AD singular-E claim in lyap/dlyap docs
The generalized-Lyapunov Notes, the scipy-path comments, and the nonsingular-E error messages stated that SLICOT SG03AD "also handles singular E". It does not: SG03AD factors the matrix pencil without inverting E (its advantage for a nonsingular but ill-conditioned E), but a truly singular (descriptor) E returns a degenerate-pair warning and is out of scope for both the scipy and slycot paths. Correct all six spots (continuous lyap and discrete dlyap) to say so. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c1ab92c commit ef138a6

1 file changed

Lines changed: 26 additions & 18 deletions

File tree

control/mateqn.py

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,13 @@ def lyap(A, Q, C=None, E=None, method=None):
125125
-----
126126
For the generalized Lyapunov equation, method='slycot' uses the
127127
SLICOT routine SG03AD, based on the generalized Schur method of
128-
Penzl [1]_, which also handles singular E. With method='scipy', the
129-
equation is transformed to a standard Lyapunov equation by inverting
130-
E, which requires E to be nonsingular and loses accuracy when E is
131-
ill-conditioned (a UserWarning is then issued); method='slycot' does
132-
not invert E and is preferable in that case.
128+
Penzl [1]_, which factors the matrix pencil without inverting E.
129+
With method='scipy', the equation is transformed to a standard
130+
Lyapunov equation by inverting E, which requires E to be nonsingular
131+
and loses accuracy when E is ill-conditioned (a UserWarning is then
132+
issued); method='slycot' does not invert E and is preferable in that
133+
case. Both methods require E nonsingular; a truly singular
134+
(descriptor) E is not currently handled by either.
133135
134136
References
135137
----------
@@ -200,16 +202,18 @@ def lyap(A, Q, C=None, E=None, method=None):
200202
#
201203
# (E^-1 A) X + X (E^-1 A)^T + E^-1 Q E^-T = 0
202204
#
203-
# This requires E to be nonsingular; the SLICOT routine
204-
# SG03AD used by method='slycot' (based on the generalized
205-
# Schur method of Penzl (1998)) also handles singular E.
205+
# This requires E to be nonsingular. SG03AD (method='slycot',
206+
# Penzl's generalized Schur method) factors the pencil without
207+
# inverting E, but a truly singular E is not handled by either
208+
# method.
206209
try:
207210
At = solve(E, A)
208211
Qt = solve(E, solve(E, Q).T).T
209212
except np.linalg.LinAlgError:
210213
raise ControlArgument(
211214
"method='scipy' requires E to be nonsingular; "
212-
"use method='slycot' (SLICOT sg03ad) for singular E")
215+
"a truly singular E (descriptor system) is not "
216+
"supported by either method")
213217
_warn_ill_conditioned_E(E)
214218
return sp.linalg.solve_continuous_lyapunov(At, -Qt)
215219

@@ -281,11 +285,13 @@ def dlyap(A, Q, C=None, E=None, method=None):
281285
-----
282286
For the generalized Lyapunov equation, method='slycot' uses the
283287
SLICOT routine SG03AD, based on the generalized Schur method of
284-
Penzl [1]_, which also handles singular E. With method='scipy', the
285-
equation is transformed to a standard Lyapunov equation by inverting
286-
E, which requires E to be nonsingular and loses accuracy when E is
287-
ill-conditioned (a UserWarning is then issued); method='slycot' does
288-
not invert E and is preferable in that case.
288+
Penzl [1]_, which factors the matrix pencil without inverting E.
289+
With method='scipy', the equation is transformed to a standard
290+
Lyapunov equation by inverting E, which requires E to be nonsingular
291+
and loses accuracy when E is ill-conditioned (a UserWarning is then
292+
issued); method='slycot' does not invert E and is preferable in that
293+
case. Both methods require E nonsingular; a truly singular
294+
(descriptor) E is not currently handled by either.
289295
290296
For the Sylvester equation, method='slycot' uses the
291297
Hessenberg-Schur method of the SLICOT routine SB04QD [2]_ and
@@ -403,16 +409,18 @@ def dlyap(A, Q, C=None, E=None, method=None):
403409
#
404410
# (E^-1 A) X (E^-1 A)^T - X + E^-1 Q E^-T = 0
405411
#
406-
# This requires E to be nonsingular; the SLICOT routine
407-
# SG03AD used by method='slycot' (based on the generalized
408-
# Schur method of Penzl (1998)) also handles singular E.
412+
# This requires E to be nonsingular. SG03AD (method='slycot',
413+
# Penzl's generalized Schur method) factors the pencil without
414+
# inverting E, but a truly singular E is not handled by either
415+
# method.
409416
try:
410417
At = solve(E, A)
411418
Qt = solve(E, solve(E, Q).T).T
412419
except np.linalg.LinAlgError:
413420
raise ControlArgument(
414421
"method='scipy' requires E to be nonsingular; "
415-
"use method='slycot' (SLICOT sg03ad) for singular E")
422+
"a truly singular E (descriptor system) is not "
423+
"supported by either method")
416424
_warn_ill_conditioned_E(E)
417425
return sp.linalg.solve_discrete_lyapunov(At, Qt)
418426

0 commit comments

Comments
 (0)