Skip to content

Commit a7f1de0

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents a6cfc93 + 6d51358 commit a7f1de0

79 files changed

Lines changed: 10668 additions & 8212 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.coveragerc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[run]
22
source = control
33
omit = control/tests/*
4+
relative_files = True
45

56
[report]
67
exclude_lines =
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Slycot from source
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
build-linux:
7+
runs-on: ubuntu-latest
8+
9+
steps:
10+
- uses: actions/checkout@v2
11+
- name: Set up Python
12+
uses: actions/setup-python@v2
13+
- name: Install Python dependencies
14+
run: |
15+
# Set up conda
16+
echo $CONDA/bin >> $GITHUB_PATH
17+
18+
# Set up (virtual) X11
19+
sudo apt install -y xvfb
20+
21+
# Install test tools
22+
conda install pip pytest
23+
24+
# Install python-control dependencies
25+
conda install numpy matplotlib scipy
26+
27+
- name: Install slycot from source
28+
run: |
29+
# Install compilers, libraries, and development environment
30+
sudo apt-get -y install gfortran cmake --fix-missing
31+
sudo apt-get -y install libblas-dev liblapack-dev
32+
conda install -c conda-forge scikit-build;
33+
34+
# Compile and install slycot
35+
git clone https://github.com/python-control/Slycot.git slycot
36+
cd slycot; python setup.py build_ext install -DBLA_VENDOR=Generic
37+
38+
- name: Test with pytest
39+
run: xvfb-run --auto-servernum pytest control/tests
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: Conda-based pytest
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
test-linux:
7+
name: Python ${{ matrix.python-version }}${{ matrix.slycot && format(' with Slycot from {0}', matrix.slycot) || ' without Slycot' }}${{ matrix.array-and-matrix == 1 && ', array and matrix' || '' }}
8+
runs-on: ubuntu-latest
9+
10+
strategy:
11+
max-parallel: 5
12+
matrix:
13+
python-version: [3.6, 3.9]
14+
slycot: ["", "conda"]
15+
array-and-matrix: [0]
16+
include:
17+
- python-version: 3.9
18+
slycot: conda
19+
array-and-matrix: 1
20+
21+
steps:
22+
- uses: actions/checkout@v2
23+
24+
- name: Install dependencies
25+
run: |
26+
# Set up conda
27+
echo $CONDA/bin >> $GITHUB_PATH
28+
conda create -q -n test-environment python=${{matrix.python-version}}
29+
source $CONDA/bin/activate test-environment
30+
31+
# Set up (virtual) X11
32+
sudo apt install -y xvfb
33+
34+
# Install test tools
35+
conda install pip coverage pytest
36+
pip install coveralls
37+
38+
# Install python-control dependencies
39+
conda install numpy matplotlib scipy
40+
if [[ '${{matrix.slycot}}' == 'conda' ]]; then
41+
conda install -c conda-forge slycot
42+
fi
43+
44+
- name: Test with pytest
45+
env:
46+
PYTHON_CONTROL_ARRAY_AND_MATRIX: ${{ matrix.array-and-matrix }}
47+
run: |
48+
source $CONDA/bin/activate test-environment
49+
# Use xvfb-run instead of pytest-xvfb to get proper mpl backend
50+
# Use coverage instead of pytest-cov to get .coverage file
51+
# See https://github.com/python-control/python-control/pull/504
52+
xvfb-run --auto-servernum coverage run -m pytest control/tests
53+
54+
- name: Coveralls parallel
55+
# https://github.com/coverallsapp/github-action
56+
uses: AndreMiras/coveralls-python-action@develop
57+
with:
58+
parallel: true
59+
60+
coveralls:
61+
name: coveralls completion
62+
needs: test-linux
63+
runs-on: ubuntu-latest
64+
steps:
65+
- name: Coveralls Finished
66+
uses: AndreMiras/coveralls-python-action@develop
67+
with:
68+
parallel-finished: true

.travis.yml

Lines changed: 15 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -12,73 +12,35 @@ cache:
1212
- $HOME/.cache/pip
1313
- $HOME/.local
1414

15+
# Test against earliest supported (Python 3) release and latest stable release
1516
python:
16-
- "3.7"
17+
- "3.9"
1718
- "3.6"
18-
- "2.7"
1919

20-
# Test against multiple version of SciPy, with and without slycot
21-
#
22-
# Because there were significant changes in SciPy between v0 and v1, we
23-
# test against both of these using the Travis CI environment capability
24-
#
25-
# We also want to test with and without slycot
2620
env:
2721
- SCIPY=scipy SLYCOT=conda # default, with slycot via conda
2822
- SCIPY=scipy SLYCOT= # default, w/out slycot
29-
- SCIPY="scipy==0.19.1" SLYCOT= # legacy support, w/out slycot
3023

3124
# Add optional builds that test against latest version of slycot, python
3225
jobs:
3326
include:
34-
- name: "linux, Python 2.7, slycot=source"
35-
os: linux
36-
dist: xenial
37-
services: xvfb
27+
- name: "Python 3.9, slycot=source, array and matrix"
28+
python: "3.9"
29+
env: SCIPY=scipy SLYCOT=source PYTHON_CONTROL_ARRAY_AND_MATRIX=1
30+
# Because there were significant changes in SciPy between v0 and v1, we
31+
# also test against the latest v0 (without Slycot) for old pythons.
32+
# newer pythons should always use newer SciPy.
33+
- name: "Python 2.7, Scipy 0.19.1"
3834
python: "2.7"
39-
env: SCIPY=scipy SLYCOT=source
40-
- name: "linux, Python 3.7, slycot=source"
41-
os: linux
42-
dist: xenial
43-
services: xvfb
44-
python: "3.7"
45-
env: SCIPY=scipy SLYCOT=source
46-
- name: "linux, Python 3.8, slycot=source"
47-
os: linux
48-
dist: xenial
49-
services: xvfb
50-
python: "3.8"
51-
env: SCIPY=scipy SLYCOT=source
52-
- name: "use numpy matrix"
53-
dist: xenial
54-
services: xvfb
55-
python: "3.8"
56-
env: SCIPY=scipy SLYCOT=source PYTHON_CONTROL_STATESPACE_ARRAY=1
57-
58-
# Exclude combinations that are very unlikely (and don't work)
59-
exclude:
60-
- python: "3.7" # python3.7 should use latest scipy
35+
env: SCIPY="scipy==0.19.1" SLYCOT=
36+
- name: "Python 3.6, Scipy 0.19.1"
37+
python: "3.6"
6138
env: SCIPY="scipy==0.19.1" SLYCOT=
6239

6340
allow_failures:
64-
- name: "linux, Python 2.7, slycot=source"
65-
os: linux
66-
dist: xenial
67-
services: xvfb
68-
python: "2.7"
69-
env: SCIPY=scipy SLYCOT=source
70-
- name: "linux, Python 3.7, slycot=source"
71-
os: linux
72-
dist: xenial
73-
services: xvfb
74-
python: "3.7"
75-
env: SCIPY=scipy SLYCOT=source
76-
- name: "linux, Python 3.8, slycot=source"
77-
os: linux
78-
dist: xenial
79-
services: xvfb
80-
python: "3.8"
81-
env: SCIPY=scipy SLYCOT=source
41+
- env: SCIPY=scipy SLYCOT=source
42+
- env: SCIPY=scipy SLYCOT=source PYTHON_CONTROL_ARRAY_AND_MATRIX=1
43+
8244

8345
# install required system libraries
8446
before_install:

README.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,7 @@ Your contributions are welcome! Simply fork the GitHub repository and send a
131131

132132
.. _pull request: https://github.com/python-control/python-control/pulls
133133

134+
Please see the `Developer's Wiki`_ for detailed instructions.
135+
136+
.. _Developer's Wiki: https://github.com/python-control/python-control/wiki
137+

control/bdalg.py

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def series(sys1, *sysn):
7676
Raises
7777
------
7878
ValueError
79-
if `sys2.inputs` does not equal `sys1.outputs`
79+
if `sys2.ninputs` does not equal `sys1.noutputs`
8080
if `sys1.dt` is not compatible with `sys2.dt`
8181
8282
See Also
@@ -242,9 +242,9 @@ def feedback(sys1, sys2=1, sign=-1):
242242
if isinstance(sys2, tf.TransferFunction):
243243
sys1 = tf._convert_to_transfer_function(sys1)
244244
elif isinstance(sys2, ss.StateSpace):
245-
sys1 = ss._convertToStateSpace(sys1)
245+
sys1 = ss._convert_to_statespace(sys1)
246246
elif isinstance(sys2, frd.FRD):
247-
sys1 = frd._convertToFRD(sys1, sys2.omega)
247+
sys1 = frd._convert_to_FRD(sys1, sys2.omega)
248248
else: # sys2 is a scalar.
249249
sys1 = tf._convert_to_transfer_function(sys1)
250250
sys2 = tf._convert_to_transfer_function(sys2)
@@ -302,14 +302,16 @@ def connect(sys, Q, inputv, outputv):
302302
sys : StateSpace Transferfunction
303303
System to be connected
304304
Q : 2D array
305-
Interconnection matrix. First column gives the input to be connected
306-
second column gives the output to be fed into this input. Negative
307-
values for the second column mean the feedback is negative, 0 means
308-
no connection is made. Inputs and outputs are indexed starting at 1.
305+
Interconnection matrix. First column gives the input to be connected.
306+
The second column gives the index of an output that is to be fed into
307+
that input. Each additional column gives the index of an additional
308+
input that may be optionally added to that input. Negative
309+
values mean the feedback is negative. A zero value is ignored. Inputs
310+
and outputs are indexed starting at 1 to communicate sign information.
309311
inputv : 1D array
310-
list of final external inputs
312+
list of final external inputs, indexed starting at 1
311313
outputv : 1D array
312-
list of final external outputs
314+
list of final external outputs, indexed starting at 1
313315
314316
Returns
315317
-------
@@ -324,21 +326,47 @@ def connect(sys, Q, inputv, outputv):
324326
>>> Q = [[1, 2], [2, -1]] # negative feedback interconnection
325327
>>> sysc = connect(sys, Q, [2], [1, 2])
326328
329+
Notes
330+
-----
331+
The :func:`~control.interconnect` function in the
332+
:ref:`input/output systems <iosys-module>` module allows the use
333+
of named signals and provides an alternative method for
334+
interconnecting multiple systems.
335+
327336
"""
337+
inputv, outputv, Q = np.asarray(inputv), np.asarray(outputv), np.asarray(Q)
338+
# check indices
339+
index_errors = (inputv - 1 > sys.ninputs) | (inputv < 1)
340+
if np.any(index_errors):
341+
raise IndexError(
342+
"inputv index %s out of bounds" % inputv[np.where(index_errors)])
343+
index_errors = (outputv - 1 > sys.noutputs) | (outputv < 1)
344+
if np.any(index_errors):
345+
raise IndexError(
346+
"outputv index %s out of bounds" % outputv[np.where(index_errors)])
347+
index_errors = (Q[:,0:1] - 1 > sys.ninputs) | (Q[:,0:1] < 1)
348+
if np.any(index_errors):
349+
raise IndexError(
350+
"Q input index %s out of bounds" % Q[np.where(index_errors)])
351+
index_errors = (np.abs(Q[:,1:]) - 1 > sys.noutputs)
352+
if np.any(index_errors):
353+
raise IndexError(
354+
"Q output index %s out of bounds" % Q[np.where(index_errors)])
355+
328356
# first connect
329-
K = np.zeros((sys.inputs, sys.outputs))
357+
K = np.zeros((sys.ninputs, sys.noutputs))
330358
for r in np.array(Q).astype(int):
331359
inp = r[0]-1
332360
for outp in r[1:]:
333-
if outp > 0 and outp <= sys.outputs:
334-
K[inp,outp-1] = 1.
335-
elif outp < 0 and -outp >= -sys.outputs:
361+
if outp < 0:
336362
K[inp,-outp-1] = -1.
363+
elif outp > 0:
364+
K[inp,outp-1] = 1.
337365
sys = sys.feedback(np.array(K), sign=1)
338366

339367
# now trim
340-
Ytrim = np.zeros((len(outputv), sys.outputs))
341-
Utrim = np.zeros((sys.inputs, len(inputv)))
368+
Ytrim = np.zeros((len(outputv), sys.noutputs))
369+
Utrim = np.zeros((sys.ninputs, len(inputv)))
342370
for i,u in enumerate(inputv):
343371
Utrim[u-1,i] = 1.
344372
for i,y in enumerate(outputv):

control/bench/time_freqresp.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
sys_tf = tf(sys)
99
w = logspace(-1,1,50)
1010
ntimes = 1000
11-
time_ss = timeit("sys.freqresp(w)", setup="from __main__ import sys, w", number=ntimes)
12-
time_tf = timeit("sys_tf.freqresp(w)", setup="from __main__ import sys_tf, w", number=ntimes)
11+
time_ss = timeit("sys.freqquency_response(w)", setup="from __main__ import sys, w", number=ntimes)
12+
time_tf = timeit("sys_tf.frequency_response(w)", setup="from __main__ import sys_tf, w", number=ntimes)
1313
print("State-space model on %d states: %f" % (nstates, time_ss))
1414
print("Transfer-function model on %d states: %f" % (nstates, time_tf))

0 commit comments

Comments
 (0)