Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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


.. _`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