Skip to content

Feature enable doctest #868

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 27, 2023
19 changes: 19 additions & 0 deletions .github/conda-env/doctest-env.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: test-env
dependencies:
- conda-build # for conda index
- pip
- coverage
- coveralls
- pytest
- pytest-cov
- pytest-timeout
- pytest-xvfb
- numpy
- matplotlib
- scipy
- sphinx
- sphinx_rtd_theme
- ipykernel
- nbsphinx
- docutils
- numpydoc
47 changes: 47 additions & 0 deletions .github/workflows/doctest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Doctest

on: [push, pull_request]

jobs:
doctest-linux:
# doctest needs to run only on
# latest-greatest platform with full options
runs-on: ubuntu-latest

steps:
- name: Checkout python-control
uses: actions/checkout@v3

- name: Setup Conda
uses: conda-incubator/setup-miniconda@v2
with:
python-version: 3.11
activate-environment: test-env
environment-file: .github/conda-env/doctest-env.yml
miniforge-version: latest
miniforge-variant: Mambaforge
channels: conda-forge
channel-priority: strict
auto-update-conda: false
auto-activate-base: false

- name: Install full dependencies
shell: bash -l {0}
run: |
mamba install cvxopt pandas slycot

- name: Run doctest
shell: bash -l {0}
env:
PYTHON_CONTROL_ARRAY_AND_MATRIX: ${{ matrix.array-and-matrix }}
MPLBACKEND: ${{ matrix.mplbackend }}
working-directory: doc
run: |
make html
make doctest

- name: Archive results
uses: actions/upload-artifact@v3
with:
name: doctest-output
path: doc/_build/doctest/output.txt
17 changes: 17 additions & 0 deletions control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@
"""
The Python Control Systems Library :mod:`control` provides common functions
for analyzing and designing feedback control systems.

Documentation is available in two forms: docstrings provided with the code,
and the python-control users guide, available from `the python-control
homepage <https://www.python-control.org>`_.

The docstring examples assume that the following import commands::

>>> import numpy as np
>>> import control as ct

Available subpackages
---------------------
flatsys
Differentially flat systems
optimal
Optimization-based control

"""

# Import functions from within the control system library
Expand Down
68 changes: 53 additions & 15 deletions control/bdalg.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,17 @@ def series(sys1, *sysn):

Examples
--------
>>> sys3 = series(sys1, sys2) # Same as sys3 = sys2 * sys1

>>> sys5 = series(sys1, sys2, sys3, sys4) # More systems
>>> G1 = ct.rss(3)
>>> G2 = ct.rss(4)
>>> G = ct.series(G1, G2) # Same as sys3 = sys2 * sys1
>>> G.ninputs, G.noutputs, G.nstates
(1, 1, 7)

>>> G1 = ct.rss(2, inputs=2, outputs=3)
>>> G2 = ct.rss(3, inputs=3, outputs=1)
>>> G = ct.series(G1, G2) # Same as sys3 = sys2 * sys1
>>> G.ninputs, G.noutputs, G.nstates
(2, 1, 5)

"""
from functools import reduce
Expand Down Expand Up @@ -146,9 +154,17 @@ def parallel(sys1, *sysn):

Examples
--------
>>> sys3 = parallel(sys1, sys2) # Same as sys3 = sys1 + sys2

>>> sys5 = parallel(sys1, sys2, sys3, sys4) # More systems
>>> G1 = ct.rss(3)
>>> G2 = ct.rss(4)
>>> G = ct.parallel(G1, G2) # Same as sys3 = sys1 + sys2
>>> G.ninputs, G.noutputs, G.nstates
(1, 1, 7)

>>> G1 = ct.rss(3, inputs=3, outputs=4)
>>> G2 = ct.rss(4, inputs=3, outputs=4)
>>> G = ct.parallel(G1, G2) # Add another system
>>> G.ninputs, G.noutputs, G.nstates
(3, 4, 7)

"""
from functools import reduce
Expand All @@ -174,7 +190,13 @@ def negate(sys):

Examples
--------
>>> sys2 = negate(sys1) # Same as sys2 = -sys1.
>>> G = ct.tf([2], [1, 1])
>>> G.dcgain()
2.0

>>> Gn = ct.negate(G) # Same as sys2 = -sys1.
>>> Gn.dcgain()
-2.0

"""
return -sys
Expand Down Expand Up @@ -222,6 +244,14 @@ def feedback(sys1, sys2=1, sign=-1):
the corresponding feedback function is used. If `sys1` and `sys2` are both
scalars, then TransferFunction.feedback is used.

Examples
--------
>>> G = ct.rss(3, inputs=2, outputs=5)
>>> C = ct.rss(4, inputs=5, outputs=2)
>>> T = ct.feedback(G, C, sign=1)
>>> T.ninputs, T.noutputs, T.nstates
(2, 5, 7)

"""
# Allow anything with a feedback function to call that function
try:
Expand Down Expand Up @@ -278,9 +308,17 @@ def append(*sys):

Examples
--------
>>> sys1 = ss([[1., -2], [3., -4]], [[5.], [7]], [[6., 8]], [[9.]])
>>> sys2 = ss([[-1.]], [[1.]], [[1.]], [[0.]])
>>> sys = append(sys1, sys2)
>>> G1 = ct.rss(3)
>>> G2 = ct.rss(4)
>>> G = ct.append(G1, G2)
>>> G.ninputs, G.noutputs, G.nstates
(2, 2, 7)

>>> G1 = ct.rss(3, inputs=2, outputs=4)
>>> G2 = ct.rss(4, inputs=1, outputs=4)
>>> G = ct.append(G1, G2)
>>> G.ninputs, G.noutputs, G.nstates
(3, 8, 7)

"""
s1 = ss._convert_to_statespace(sys[0])
Expand Down Expand Up @@ -323,11 +361,11 @@ def connect(sys, Q, inputv, outputv):

Examples
--------
>>> sys1 = ss([[1., -2], [3., -4]], [[5.], [7]], [[6, 8]], [[9.]])
>>> sys2 = ss([[-1.]], [[1.]], [[1.]], [[0.]])
>>> sys = append(sys1, sys2)
>>> Q = [[1, 2], [2, -1]] # negative feedback interconnection
>>> sysc = connect(sys, Q, [2], [1, 2])
>>> G = ct.rss(7, inputs=2, outputs=2)
>>> K = [[1, 2], [2, -1]] # negative feedback interconnection
>>> T = ct.connect(G, K, [2], [1, 2])
>>> T.ninputs, T.noutputs, T.nstates
(1, 2, 7)

Notes
-----
Expand Down
20 changes: 11 additions & 9 deletions control/bench/time_freqresp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
from numpy import logspace
from timeit import timeit

nstates = 10
sys = rss(nstates)
sys_tf = tf(sys)
w = logspace(-1,1,50)
ntimes = 1000
time_ss = timeit("sys.freqquency_response(w)", setup="from __main__ import sys, w", number=ntimes)
time_tf = timeit("sys_tf.frequency_response(w)", setup="from __main__ import sys_tf, w", number=ntimes)
print("State-space model on %d states: %f" % (nstates, time_ss))
print("Transfer-function model on %d states: %f" % (nstates, time_tf))

if __name__ == '__main__':
nstates = 10
sys = rss(nstates)
sys_tf = tf(sys)
w = logspace(-1,1,50)
ntimes = 1000
time_ss = timeit("sys.freqquency_response(w)", setup="from __main__ import sys, w", number=ntimes)
time_tf = timeit("sys_tf.frequency_response(w)", setup="from __main__ import sys_tf, w", number=ntimes)
print("State-space model on %d states: %f" % (nstates, time_ss))
print("Transfer-function model on %d states: %f" % (nstates, time_tf))
Loading