Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions BUILD.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ The test command is the same as when building from the sdist::
Non-isolated, editable build
----------------------------

When doing development one builds over and over; in that case it's faster to do a non-isolated editable build. Run or adapt developer script `inplace-editable-build.bash`_ for that.
When doing development one builds over and over; in that case it's faster to do a non-isolated editable build. Run or adapt developer script `inplace-editable-build.bash`_ for that. CMake variables can be set to configure the build; for example, in the command below, ``BLA_VENDOR`` is set to to tell CMake to use the Netlib BLAS/LAPACK libraries. This example is not merely illustrative: on Debian 13 *not* specifying ``BLA_VENDOR`` can cause test failures.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
When doing development one builds over and over; in that case it's faster to do a non-isolated editable build. Run or adapt developer script `inplace-editable-build.bash`_ for that. CMake variables can be set to configure the build; for example, in the command below, ``BLA_VENDOR`` is set to to tell CMake to use the Netlib BLAS/LAPACK libraries. This example is not merely illustrative: on Debian 13 *not* specifying ``BLA_VENDOR`` can cause test failures.
When doing development one builds over and over; in that case it's faster to do a non-isolated editable build. Run or adapt developer script `inplace-editable-build.bash`_ for that. CMake variables can be set to configure the build; for example, in the command below, ``BLA_VENDOR`` is set to tell CMake to use the Netlib BLAS/LAPACK libraries. This example is not merely illustrative: on Debian 13 *not* specifying ``BLA_VENDOR`` can cause test failures.


.. _`inplace-editable-build.bash`: ./dev-tools/inplace-editable-build.bash
.. _`inplace-editable-build.bash`: BLA_VENDOR=Generic ./dev-tools/inplace-editable-build.bash


Customizing the build
Expand Down
7 changes: 7 additions & 0 deletions dev-tools/inplace-editable-build.bash
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
# - BLAS and LAPACK libraries
#
# Tested on Debian 13.
#
# Select a BLAS/LAPACK provider with environment variable BLA_VENDOR; see
# https://cmake.org/cmake/help/latest/module/FindBLAS.html#blas-lapack-vendors

set -euo pipefail

Expand All @@ -28,6 +31,10 @@ if [ ! -f ./dev-tools/inplace-editable-build.bash ]; then
exit 1;
fi

if [ ! -v BLA_VENDOR ]; then
echo "BLA_VENDOR is not set. This is known to sometimes cause test failures";
fi

echo "--Build"
python -m venv venv-build
source venv-build/bin/activate
Expand Down
11 changes: 8 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[build-system]
requires = ["scikit-build-core", "numpy"]
requires = ["scikit-build-core>=1.0", "numpy"]
build-backend = "scikit_build_core.build"

[project]
Expand Down Expand Up @@ -43,16 +43,21 @@ test = ["pytest", "scipy"]
[project.urls]
homepage = "https://github.com/python-control/Slycot"

[[tool.dynamic-metadata]]
field = "version"
provider = "scikit_build_core.metadata.setuptools_scm"

[tool.scikit-build]
metadata.version.provider = "scikit_build_core.metadata.setuptools_scm"
wheel.exclude = ["slycot/src/", "slycot/CMakeLists.txt", "slycot/slicot-source.cmake", "slycot/scipy-openblas-symbols"]

[tool.pytest.ini_options]
# run the tests with compiled and installed package
addopts = "--pyargs slycot"

[tool.ruff.lint]
ignore = [ "E741" ]
ignore = [
"I001", # import order & formatting
]

[tool.cibuildwheel]
# scipy-openblas32 version specified here
Expand Down
18 changes: 9 additions & 9 deletions slycot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,15 @@


__all__ = [
ab01nd, ab04md, ab05md, ab05nd, ab07nd, ab08nd, ab08nz,
ab09ad, ab09ax, ab09bd, ab09md, ab09nd, ab13bd, ab13dd,
ab13ed, ab13fd, ab13md, ag08bd, mb02ed, mb03rd, mb03vd,
mb03vy, mb03wd, mb05md, mb05nd, mc01td, sb01bd, sb02md,
sb02mt, sb02od, sb03md, sb03md57, sb03od, sb04md, sb04qd,
sb10ad, sb10dd, sb10fd, sb10hd, sb10jd, sb10yd, sg02ad,
sg03ad, sg03bd, tb01id, tb01pd, tb03ad, tb04ad, tb05ad,
tc01od, tc04ad, td04ad, tf01md, tf01rd, tg01ad, tg01fd,
__version__
'__version__',
'ab01nd', 'ab04md', 'ab05md', 'ab05nd', 'ab07nd', 'ab08nd', 'ab08nz',
'ab09ad', 'ab09ax', 'ab09bd', 'ab09md', 'ab09nd', 'ab13bd', 'ab13dd',
'ab13ed', 'ab13fd', 'ab13md', 'ag08bd', 'mb02ed', 'mb03rd', 'mb03vd',
'mb03vy', 'mb03wd', 'mb05md', 'mb05nd', 'mc01td', 'sb01bd', 'sb02md',
'sb02mt', 'sb02od', 'sb03md', 'sb03md57', 'sb03od', 'sb04md', 'sb04qd',
'sb10ad', 'sb10dd', 'sb10fd', 'sb10hd', 'sb10jd', 'sb10yd', 'sg02ad',
'sg03ad', 'sg03bd', 'tb01id', 'tb01pd', 'tb03ad', 'tb04ad', 'tb05ad',
'tc01od', 'tc04ad', 'td04ad', 'tf01md', 'tf01rd', 'tg01ad', 'tg01fd',
]

def test():
Expand Down
5 changes: 5 additions & 0 deletions slycot/examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,13 @@ def tb03ad_example():
print('is the following:' )
print('index', out[4])
k_max = max(out[4]) + 1
# ruff: disable[PIE808]
for k in range(0,k_max):
# ruff: disable[UP031]
print('P_%d =' %(k))
print(out[5][0:m,0:m,k])
for k in range(0,k_max):
# ruff: disable[UP031]
print('Q_%d =' %(k))
print(out[6][0:m,0:p,k])

Expand Down Expand Up @@ -254,13 +257,15 @@ def tb05ad_example():
p = np.shape(C)[0]

jw_s = [1j*11, 1j*15]
# ruff: disable[RUF059]
at, bt, ct, g_1, hinvb,info = slycot.tb05ad(n, m, p, jw_s[0],
A, B, C, job='NG')
g_2, hinv2, info = slycot.tb05ad(n, m, p, jw_s[1], at, bt, ct, job='NH')
print('--- Example for tb05ad...')
print('Frequency response for (A, B, C)')
print('-------------------------')
print('Frequency | Response')
# ruff: disable[UP031]
print('%s | %s '%(jw_s[0], g_1[0, 0]))
print('%s | %s '%(jw_s[1], g_2[0, 0]))

Expand Down
13 changes: 10 additions & 3 deletions slycot/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class SlycotError(RuntimeError):
"""Slycot exception base class"""

def __init__(self, message, info):
# ruff: disable[UP008]
super(SlycotError, self).__init__(message)
self.info = info

Expand All @@ -37,20 +38,21 @@ class SlycotParameterError(SlycotError, ValueError):
In case of a wrong input value, the SLICOT routines return a negative
info parameter indicating which parameter was illegal.
"""

# ruff: disable[PIE790]
pass


class SlycotArithmeticError(SlycotError, ArithmeticError):
"""A Slycot computation failed"""

# ruff: disable[PIE790]
pass


class SlycotWarning(UserWarning):
"""Slycot Warning"""

def __init__(self, message, iwarn, info):
# ruff: disable[UP008]
super(SlycotWarning, self).__init__(message)
self.info = info
self.iwarn = iwarn
Expand All @@ -62,7 +64,7 @@ class SlycotResultWarning(SlycotWarning):
A Slycot routine returned a nonzero info parameter that warns about the
returned results, but the results might still be usable.
"""

# ruff: disable[PIE790]
pass


Expand Down Expand Up @@ -101,7 +103,9 @@ def _parse_docsection(section_name, docstring, checkvars):
infospec_indent = len(imatch.group(1))
infospec = imatch.group(2)
# Don't handle the standard case unless we have i
# ruff: disable[SIM102]
if infospec == "info = -i":
# ruff: disable[SIM118]
if 'i' not in checkvars.keys():
continue
infospec_ = infospec.replace(" = ", " == ")
Expand Down Expand Up @@ -242,18 +246,21 @@ def raise_if_slycot_error(info, arg_list=None, docstring=None, checkvars=None):
return

if info < 0 and arg_list:
# ruff: disable[UP032]
message = ("The following argument had an illegal value: {}"
"".format(arg_list[-info-1]))
raise SlycotParameterError(message, info)

# catch all
if info > 0:
# ruff: disable[UP032]
raise SlycotError("Caught unhandled nonzero INFO value {}"
"".format(info),
info)
if iwarn is None and 'iwarn' in checkvars:
iwarn = checkvars['iwarn']
if iwarn:
# ruff: disable[UP032]
warn(SlycotWarning("Caught unhandled nonzero IWARN value {}"
"".format(iwarn),
iwarn, info))
1 change: 1 addition & 0 deletions slycot/scipy-openblas-symbols/generate_def.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ def main():
)

with open("scipy-openblas-symbols.def", "wt") as outfile:
# ruff: disable[FURB122]
for symbol in blas + lapack + own_symbols + lapack_exclusions:
outfile.write(f"-D{symbol.upper()}=SCIPY_{symbol.upper()}\n")

Expand Down
2 changes: 2 additions & 0 deletions slycot/synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,7 @@ def sb04qd(n,m,A,B,C,ldwork=None):
for the ({info}-{m})-th column of matrix X.
"""
hidden = ' (hidden by the wrapper)'
# ruff: disable[PLW0127, PLW0128]
arg_list = arg_list = ['n', 'm', 'A', 'LDA' + hidden, 'B', 'LDB' + hidden,
'C', 'LDC' + hidden, 'Z', 'LDZ' + hidden,
'IWORK' + hidden, 'DWORK' + hidden, 'ldwork', 'INFO' + hidden]
Expand Down Expand Up @@ -2378,6 +2379,7 @@ def sg02ad(dico,jobb,fact,uplo,jobl,scal,sort,acc,N,M,P,A,E,B,Q,R,L,ldwork=None,
elif (jobb == 'B'):
if (fact == 'N'):
out = _wrapper.sg02ad_bn(dico,uplo,jobl,scal,sort,acc,N,M,A,E,B,Q,R,L,tol,ldwork)
#ruff: disable[SIM114]
elif (fact == 'C'):
out = _wrapper.sg02ad_bc(dico,jobl,scal,sort,acc,N,M,P,A,E,B,Q,R,L,tol,ldwork)
elif (fact == 'D'):
Expand Down
1 change: 1 addition & 0 deletions slycot/tests/test_ab08n.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def ab08nX(self, ab08fun, A, B, C, D):
assert_allclose(Af[:nu, :nu], np.array([[-4.]]))
# Compute the structural invariants of the given system.
out = ab08fun(n, m, p, A, B, C, D)
# ruff: disable[RUF059]
nu, rank, dinfz, nkror, nkrol, infz, kronr, kronl, Af, Bf = out[:10]
assert_equal(nu, 2)
# Compute the invariant zeros of the given system.
Expand Down
2 changes: 2 additions & 0 deletions slycot/tests/test_ab09nd.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def test_slicot_ref():

d = np.zeros((3,2))

# ruff: disable[RUF059]
nr, ar, br, cr, dr, ns, hsv = \
ab09nd(dico, job, equil, n, m, p, a, b, c, d, alpha, nr, tol1, tol2)

Expand Down Expand Up @@ -85,6 +86,7 @@ def test_gh242_regression():
c = np.zeros((p, n))
d = np.array([[42.24]])

# ruff: disable[RUF059]
nr, ar, br, cr, dr, ns, hsv = \
ab09nd(dico='C', job='B', equil='S', n=a.shape[0],
m=b.shape[1], p=c.shape[0], A=a, B=b, C=c, D=d)
Expand Down
2 changes: 2 additions & 0 deletions slycot/tests/test_ab13md.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def test_cached_inputoutput():
m = len(nblock)
mr = np.count_nonzero(1==itype)

# ruff: disable[RUF059]
mu0, d0, g0, x0 = ab13md(Z, nblock, itype)
assert m+mr-1 == len(x0)

Expand Down Expand Up @@ -112,6 +113,7 @@ def test_slicot(self):

Z, nblock, itype = slicot_example()

# ruff: disable[RUF059]
mu, d, g, x = ab13md(Z, nblock, itype)

assert_allclose(mu, muref)
Expand Down
1 change: 1 addition & 0 deletions slycot/tests/test_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from .test_exceptions import assert_docstring_parse


#ruff: disable[PT014]
@pytest.mark.parametrize(
'fun, exception_class, erange, checkvars',
((analysis.ab05nd, SlycotArithmeticError, 1, {'p1': 1}),
Expand Down
1 change: 1 addition & 0 deletions slycot/tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def test_example(examplefun, capsys, recwarn):
(check_warn(recwarn, examplefun), "Example {} produced a warning.\n")]
for failed, msgfmt in failconditions:
if failed:
# ruff: disable[UP032]
pytest.fail(msgfmt.format(examplefun.__name__) +
"Captured output:\n{}\n"
"Captured stderr:\n{}\n"
Expand Down
5 changes: 4 additions & 1 deletion slycot/tests/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
SlycotWarning, raise_if_slycot_error)


def assert_docstring_parse(docstring, exception_class, erange, checkvars={}):
def assert_docstring_parse(docstring, exception_class, erange, checkvars):
"""To check that a docstring can be parsed into exceptions
See also raise_if_slycot_error

Expand Down Expand Up @@ -69,6 +69,7 @@ def assert_docstring_parse(docstring, exception_class, erange, checkvars={}):
assert wm[0].message.iwarn == iwarn
assert wm[0].message.info == info
else:
# ruff: disable[TRY004]
raise RuntimeError("Invalid test exception")


Expand All @@ -83,6 +84,7 @@ def test_unhandled_info_iwarn():
with pytest.raises(SlycotError) as ex_info:
raise_if_slycot_error(100, [], docstring="no valid docstring")
assert ex_info.value.info == 100
# ruff: disable[PT031]
with pytest.warns(SlycotWarning) as wm:
raise_if_slycot_error([101, 0], [], docstring="no valid docstring")
raise_if_slycot_error(0, [], docstring="no valid docstring",
Expand Down Expand Up @@ -162,6 +164,7 @@ def test_xerbla_override():
stderr=subprocess.STDOUT,
universal_newlines=True)
except subprocess.CalledProcessError as cpe:
# ruff: disable[UP032]
raise RuntimeError("Trying to call _wrapper.ab08nd() failed with "
"returncode {}.\n"
"Captured STDOUT: \n {}\n"
Expand Down
3 changes: 3 additions & 0 deletions slycot/tests/test_mb.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ def test_mb03rd():
A, X = schur(test1_A)
Ah, Xh = np.copy(A), np.copy(X)
# on this basis, get the transform
# ruff: disable[RUF059]
Ar, Xr, blsize, W = mb03rd(
test1_n, A, X, 'U', 'S', test1_pmax, test1_tol)
# ensure X and A are unchanged
Expand Down Expand Up @@ -235,6 +236,7 @@ def test_mb03rd_default():

X = Tschur.copy()

# ruff: disable[RUF059]
Ar, Xr, blsize, W = mb03rd(Aschur.shape[0], Aschur, X, 'U', 'N', pmax=1.0, tol=0.0)

Ar2, Xr2, blsize2, W2 = mb03rd(Aschur.shape[0], Aschur)
Expand Down Expand Up @@ -428,6 +430,7 @@ def test_mb05md_warning():
match="\n"
"Matrix A is defective, possibly "
"due to rounding errors.") as record:
# ruff: disable[RUF059]
(Ar, Vr, Yr, VAL) = mb05md(A, delta)
assert record[0].message.info == 6

Expand Down
2 changes: 2 additions & 0 deletions slycot/tests/test_mc.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def test_mc01td_D():

def test_mc01td_warnings():
""" test_mc01td_warnings: Test warnings """
# ruff: disable[ISC004]
T = [([0, 0], "Entry ``P(x)`` is the zero polynomial."),
([0, 1], "The polynomial ``P(x)`` is most probably unstable,\n"
"although it may be stable with one or more zeros\n"
Expand All @@ -43,4 +44,5 @@ def test_mc01td_warnings():
"for ``j = 0, 1,..., k-1`` and ``P(DB+1-k) <> 0.0``.")]
for P, m in T:
with pytest.warns(SlycotResultWarning, match=re.escape(m)):
# ruff: disable[RUF059]
(dp, stable, nz) = mc01td('C', len(P)-1, P)
1 change: 1 addition & 0 deletions slycot/tests/test_sb.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def test_sb10ad():
nmeas = 1
gamma = 10

# ruff: disable[RUF059]
gamma_est, Ak, Bk, Ck, Dk, Ac, Bc, Cc, Dc, rcond = synthesis.sb10ad(
n, m, np, ncon, nmeas, gamma, a, b, c, d)
# from Octave, which also uses SB10AD:
Expand Down
4 changes: 4 additions & 0 deletions slycot/tests/test_sb10yd.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from slycot import synthesis


# ruff: disable[UP039]
class Test_sb10yd():

# TODO: There might be better systems/filters to do these tests.
Expand Down Expand Up @@ -75,6 +76,8 @@ def test_sb10yd_cont_allclose(self):
n = 2
dico = 0 # 0 for continuous time
flag = 0 # 0 for no constraints on the poles

# ruff: disable[RUF059]
n_id, A_id, B_id, C_id, D_id = synthesis.sb10yd(
dico, flag, len(omega),
real_H_resp, imag_H_resp, omega, n, tol=0)
Expand Down Expand Up @@ -161,6 +164,7 @@ def test_sb10yd_disc_allclose(self):
n = 2
dico = 1 # 0 for discrete time
flag = 0 # 0 for no constraints on the poles
# ruff: disable[RUF059]
n_id, A_id, B_id, C_id, D_id = synthesis.sb10yd(
dico, flag, len(omega),
real_H_resp, imag_H_resp, omega, n, tol=0)
Expand Down
1 change: 1 addition & 0 deletions slycot/tests/test_sg02ad.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def test_sg02ad_case1():
[-1.94918951, -3.15480639, 5.24379117],
[ 4.29133973, 8.10585767, -5.88895897]])
L = np.array(np.zeros((3,1)))
# ruff: disable[RUF059]
rcondu, X, alphar, alphai, beta, S, T, U, iwarn = \
synthesis.sg02ad('D', 'B', 'N', 'U', 'Z', 'N', 'S', 'R',
n, m, 1,
Expand Down
Loading
Loading