Skip to content
Closed
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
3 changes: 2 additions & 1 deletion .github/workflows/doctest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ jobs:
- name: Install full dependencies
shell: bash -l {0}
run: |
mamba install cvxopt pandas slycot
mamba install cvxopt pandas
pip install 'slicot>=1.0.12'

- name: Run doctest
shell: bash -l {0}
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/install_examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ jobs:
--quiet --yes \
python=3.12 pip \
numpy matplotlib scipy \
slycot pmw jupyter \
pmw jupyter \
ipython!=9.0
conda run -n control-examples-env pip install 'slicot>=1.0.12'

- name: Install from source
run: |
Expand Down
17 changes: 15 additions & 2 deletions .github/workflows/python-package-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
test-linux-conda:
name: >
Py${{ matrix.python-version }};
${{ matrix.slycot || 'no' }} Slycot;
${{ matrix.slycot && 'slycot' || matrix.slicot && 'slicot' || 'no-slicot' }};
${{ matrix.pandas || 'no' }} Pandas;
${{ matrix.cvxopt || 'no' }} CVXOPT
${{ matrix.mplbackend && format('; {0}', matrix.mplbackend) }}
Expand All @@ -18,6 +18,7 @@ jobs:
matrix:
python-version: ['3.10', '3.12']
slycot: ["", "conda"]
slicot: [""]
pandas: [""]
cvxopt: ["", "conda"]
mplbackend: [""]
Expand All @@ -27,6 +28,15 @@ jobs:
pandas: conda
cvxopt: conda
mplbackend: QtAgg
# Test with slicot (pip) instead of slycot (conda)
- python-version: '3.12'
slicot: pip
slycot: ""
cvxopt: conda
exclude:
# Don't test both slycot and slicot together
- slycot: "conda"
slicot: "pip"

steps:
- uses: actions/checkout@v3
Expand All @@ -52,6 +62,9 @@ jobs:
if [[ '${{matrix.slycot}}' == 'conda' ]]; then
mamba install slycot
fi
if [[ '${{matrix.slicot}}' == 'pip' ]]; then
pip install 'slicot>=1.0.12'
fi
if [[ '${{matrix.pandas}}' == 'conda' ]]; then
mamba install pandas
fi
Expand All @@ -70,7 +83,7 @@ jobs:
- name: report coverage
uses: coverallsapp/github-action@v2
with:
flag-name: conda-pytest_py${{ matrix.python-version }}_${{ matrix.slycot || 'no' }}-Slycot_${{ matrix.pandas || 'no' }}-Pandas_${{ matrix.cvxopt || 'no' }}_CVXOPT-${{ matrix.mplbackend && format('; {0}', matrix.mplbackend) }}
flag-name: conda-pytest_py${{ matrix.python-version }}_${{ matrix.slycot && 'slycot' || matrix.slicot && 'slicot' || 'no-slicot' }}_${{ matrix.pandas || 'no' }}-Pandas_${{ matrix.cvxopt || 'no' }}_CVXOPT-${{ matrix.mplbackend && format('; {0}', matrix.mplbackend) }}
parallel: true
file: coverage.xml

Expand Down
8 changes: 4 additions & 4 deletions control/canonical.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from numpy.linalg import matrix_rank, solve
from scipy.linalg import schur

from .exception import ControlNotImplemented, ControlSlycot
from .exception import ControlNotImplemented, ControlSlicot
from .iosys import issiso
from .statefbk import ctrb, obsv
from .statesp import StateSpace, _convert_to_statespace
Expand Down Expand Up @@ -330,15 +330,15 @@ def _bdschur_condmax_search(aschur, tschur, condmax):

Notes
-----
Outputs as for slycot.mb03rd.
Outputs as for slicot.mb03rd.

`aschur`, `tschur` are as returned by scipy.linalg.schur.

"""
try:
from slycot import mb03rd
from .slicot_compat import mb03rd
except ImportError:
raise ControlSlycot("can't find slycot module 'mb03rd'")
raise ControlSlicot("can't find slicot module 'mb03rd'")

# see notes on RuntimeError below
pmaxlower = None
Expand Down
52 changes: 39 additions & 13 deletions control/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,28 @@

"""Exception definitions for the control package."""

class ControlSlycot(ImportError):
"""Slycot import failed."""
import warnings


class ControlSlicot(ImportError):
"""Slicot import failed."""
pass


def _deprecated_alias(old_name, new_name):
"""Issue deprecation warning for renamed class/function."""
warnings.warn(
f"{old_name} is deprecated, use {new_name} instead",
DeprecationWarning, stacklevel=3
)


class ControlSlycot(ControlSlicot):
"""Deprecated alias for ControlSlicot."""
def __init__(self, *args, **kwargs):
_deprecated_alias('ControlSlycot', 'ControlSlicot')
super().__init__(*args, **kwargs)

class ControlDimension(ValueError):
"""Raised when dimensions of system objects are not correct."""
pass
Expand All @@ -29,18 +47,26 @@ class ControlNotImplemented(NotImplementedError):
"""Functionality is not yet implemented."""
pass

# Utility function to see if Slycot is installed
slycot_installed = None
def slycot_check():
"""Return True if Slycot is installed, otherwise False."""
global slycot_installed
if slycot_installed is None:
# Utility function to see if slicot or slycot is installed
slicot_installed = None
def slicot_check():
"""Return True if slicot or slycot is installed, otherwise False."""
global slicot_installed
if slicot_installed is None:
try:
import slycot # noqa: F401
slycot_installed = True
except:
slycot_installed = False
return slycot_installed
import slicot # noqa: F401
slicot_installed = True
except ImportError:
try:
import slycot # noqa: F401
slicot_installed = True
except ImportError:
slicot_installed = False
return slicot_installed


# Backwards-compatible alias (no warning to avoid noise in existing code)
slycot_check = slicot_check


# Utility function to see if pandas is installed
Expand Down
4 changes: 2 additions & 2 deletions control/margins.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from .iosys import issiso
from .ctrlutil import mag2db
try:
from slycot import ab13md
from .slicot_compat import ab13md
except ImportError:
ab13md = None

Expand Down Expand Up @@ -577,7 +577,7 @@ def disk_margins(L, omega, skew=0.0, returnall=False):
# Need slycot if L is MIMO, for mu calculation
if not L.issiso() and ab13md == None:
raise ControlMIMONotImplemented(
"Need slycot to compute MIMO disk_margins")
"Need slicot to compute MIMO disk_margins")

# Get dimensions of feedback system
num_loops = statesp.ss(L).C.shape[0]
Expand Down
Loading