Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
13 changes: 13 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[run]
source = control
omit = control/tests/*

[report]
exclude_lines =
# Don't complain if tests don't hit defensive assertion code:
raise AssertionError
raise NotImplementedError

# Don't complain if non-runnable code isn't run:
if 0:
if __name__ == .__main__.:
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ control/version.py
build.log
*.egg-info/
.coverage
doc/_build
39 changes: 21 additions & 18 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,33 +1,36 @@
language: python
python:
- "2.7"
- "3.2"
- "3.3"
- "3.4"

# install required system libraries
before_install:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- sudo apt-get update --fix-missing -qq
- sudo apt-get build-dep python-scipy -qq
# command to install dependencies from source
# note, separating requirements so that travis
# will get output in less than 5 min and won't
# terminate, using q to keep build info to a
# minumum for dependencies
- sudo apt-get install gfortran liblapack-dev
# use miniconda to install numpy/scipy, to avoid lengthy build from source
- if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
wget http://repo.continuum.io/miniconda/Miniconda-3.4.2-Linux-x86_64.sh -O miniconda.sh;
else
wget http://repo.continuum.io/miniconda/Miniconda3-3.4.2-Linux-x86_64.sh -O miniconda.sh;
fi
- bash miniconda.sh -b -p $HOME/miniconda
- export PATH="$HOME/miniconda/bin:$PATH"
- hash -r
- conda config --set always_yes yes --set changeps1 no
- conda update -q conda
- conda info -a

# Install packages
install:
- while [[ 1 ]]; do echo "building deps"; sleep 300; done &
- msg_pid=$!
- pip install -q coverage
- pip install -q coveralls
- pip install -q nose
- pip install -q numpy
- pip install -q scipy
- pip install -q slycot
- pip install -q matplotlib
- kill $msg_pid
- conda install --yes python=$TRAVIS_PYTHON_VERSION coverage nose numpy scipy matplotlib pip
- pip install coveralls
- pip install slycot

# command to run tests
script:
- coverage run --source=control setup.py test
- coverage run setup.py test
after_success:
- coveralls
4 changes: 4 additions & 0 deletions control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@
# Exceptions
from .exception import *

# Version information
from control.version import full_version as __version__
from control.version import git_revision as __git_revision__

# Import some of the more common (and benign) MATLAB shortcuts
# By default, don't import conflicting commands here
#! TODO (RMM, 4 Nov 2012): remove MATLAB dependencies from __init__.py
Expand Down
3 changes: 1 addition & 2 deletions control/ctrlutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,10 @@
# $Id$

# Packages that we need access to
import scipy as sp
from . import lti

# Specific functions that we use
from scipy import pi
from numpy import pi

# Utility function to unwrap an angle measurement

Expand Down
6 changes: 3 additions & 3 deletions control/freqplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
#TODO: Add MIMO bode plots.
raise NotImplementedError("Bode is currently only implemented for SISO systems.")
else:
if (omega == None):
if omega is None:
# Select a default range if none is provided
omega = default_frequency_range(syslist)

Expand Down Expand Up @@ -214,7 +214,7 @@ def nyquist_plot(syslist, omega=None, Plot=True, color='b',
syslist = (syslist,)

# Select a default range if none is provided
if (omega == None):
if omega is None:
#! TODO: think about doing something smarter for discrete
omega = default_frequency_range(syslist)

Expand Down Expand Up @@ -297,7 +297,7 @@ def gangof4_plot(P, C, omega=None):

# Select a default range if none is provided
#! TODO: This needs to be made more intelligent
if (omega == None):
if omega is None:
omega = default_frequency_range((P,C))

# Compute the senstivity functions
Expand Down
65 changes: 32 additions & 33 deletions control/mateqn.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,21 @@
def lyap(A,Q,C=None,E=None):
""" X = lyap(A,Q) solves the continuous-time Lyapunov equation

A X + X A^T + Q = 0
:math:`A X + X A^T + Q = 0`

where A and Q are square matrices of the same dimension.
Further, Q must be symmetric.

X = lyap(A,Q,C) solves the Sylvester equation

A X + X Q + C = 0
:math:`A X + X Q + C = 0`

where A and Q are square matrices.

X = lyap(A,Q,None,E) solves the generalized continuous-time
Lyapunov equation

A X E^T + E X A^T + Q = 0
:math:`A X E^T + E X A^T + Q = 0`

where Q is a symmetric matrix and A, Q and E are square matrices
of the same dimension. """
Expand All @@ -88,10 +88,10 @@ def lyap(A,Q,C=None,E=None):
if len(shape(Q)) == 1:
Q = Q.reshape(1,Q.size)

if C != None and len(shape(C)) == 1:
if C is not None and len(shape(C)) == 1:
C = C.reshape(1,C.size)

if E != None and len(shape(E)) == 1:
if E is not None and len(shape(E)) == 1:
E = E.reshape(1,E.size)

# Determine main dimensions
Expand All @@ -106,7 +106,7 @@ def lyap(A,Q,C=None,E=None):
m = size(Q,0)

# Solve standard Lyapunov equation
if C==None and E==None:
if C is None and E is None:
# Check input data for consistency
if shape(A) != shape(Q):
raise ControlArgument("A and Q must be matrices of identical \
Expand Down Expand Up @@ -139,7 +139,7 @@ def lyap(A,Q,C=None,E=None):
raise e

# Solve the Sylvester equation
elif C != None and E==None:
elif C is not None and E is None:
# Check input data for consistency
if size(A) > 1 and shape(A)[0] != shape(A)[1]:
raise ControlArgument("A must be a quadratic matrix.")
Expand Down Expand Up @@ -170,7 +170,7 @@ def lyap(A,Q,C=None,E=None):
raise e

# Solve the generalized Lyapunov equation
elif C == None and E != None:
elif C is None and E is not None:
# Check input data for consistency
if (size(Q) > 1 and shape(Q)[0] != shape(Q)[1]) or \
(size(Q) > 1 and shape(Q)[0] != n) or \
Expand Down Expand Up @@ -233,21 +233,21 @@ def lyap(A,Q,C=None,E=None):
def dlyap(A,Q,C=None,E=None):
""" dlyap(A,Q) solves the discrete-time Lyapunov equation

A X A^T - X + Q = 0
:math:`A X A^T - X + Q = 0`

where A and Q are square matrices of the same dimension. Further
Q must be symmetric.

dlyap(A,Q,C) solves the Sylvester equation

A X Q^T - X + C = 0
:math:`A X Q^T - X + C = 0`

where A and Q are square matrices.

dlyap(A,Q,None,E) solves the generalized discrete-time Lyapunov
equation

A X A^T - E X E^T + Q = 0
:math:`A X A^T - E X E^T + Q = 0`

where Q is a symmetric matrix and A, Q and E are square matrices
of the same dimension. """
Expand Down Expand Up @@ -275,10 +275,10 @@ def dlyap(A,Q,C=None,E=None):
if len(shape(Q)) == 1:
Q = Q.reshape(1,Q.size)

if C != None and len(shape(C)) == 1:
if C is not None and len(shape(C)) == 1:
C = C.reshape(1,C.size)

if E != None and len(shape(E)) == 1:
if E is not None and len(shape(E)) == 1:
E = E.reshape(1,E.size)

# Determine main dimensions
Expand All @@ -293,7 +293,7 @@ def dlyap(A,Q,C=None,E=None):
m = size(Q,0)

# Solve standard Lyapunov equation
if C==None and E==None:
if C is None and E is None:
# Check input data for consistency
if shape(A) != shape(Q):
raise ControlArgument("A and Q must be matrices of identical \
Expand Down Expand Up @@ -322,7 +322,7 @@ def dlyap(A,Q,C=None,E=None):
raise e

# Solve the Sylvester equation
elif C != None and E==None:
elif C is not None and E is None:
# Check input data for consistency
if size(A) > 1 and shape(A)[0] != shape(A)[1]:
raise ControlArgument("A must be a quadratic matrix")
Expand Down Expand Up @@ -353,7 +353,7 @@ def dlyap(A,Q,C=None,E=None):
raise e

# Solve the generalized Lyapunov equation
elif C == None and E != None:
elif C is None and E is not None:
# Check input data for consistency
if (size(Q) > 1 and shape(Q)[0] != shape(Q)[1]) or \
(size(Q) > 1 and shape(Q)[0] != n) or \
Expand Down Expand Up @@ -414,7 +414,7 @@ def care(A,B,Q,R=None,S=None,E=None):
""" (X,L,G) = care(A,B,Q,R=None) solves the continuous-time algebraic Riccati
equation

A^T X + X A - X B R^-1 B^T X + Q = 0
:math:`A^T X + X A - X B R^{-1} B^T X + Q = 0`

where A and Q are square matrices of the same dimension. Further,
Q and R are a symmetric matrices. If R is None, it is set to the
Expand All @@ -425,7 +425,7 @@ def care(A,B,Q,R=None,S=None,E=None):
(X,L,G) = care(A,B,Q,R,S,E) solves the generalized continuous-time
algebraic Riccati equation

A^T X E + E^T X A - (E^T X B + S) R^-1 (B^T X E + S^T) + Q = 0
:math:`A^T X E + E^T X A - (E^T X B + S) R^{-1} (B^T X E + S^T) + Q = 0`

where A, Q and E are square matrices of the same
dimension. Further, Q and R are symmetric matrices. If R is None,
Expand Down Expand Up @@ -460,13 +460,13 @@ def care(A,B,Q,R=None,S=None,E=None):
if len(shape(Q)) == 1:
Q = Q.reshape(1,Q.size)

if R != None and len(shape(R)) == 1:
if R is not None and len(shape(R)) == 1:
R = R.reshape(1,R.size)

if S != None and len(shape(S)) == 1:
if S is not None and len(shape(S)) == 1:
S = S.reshape(1,S.size)

if E != None and len(shape(E)) == 1:
if E is not None and len(shape(E)) == 1:
E = E.reshape(1,E.size)

# Determine main dimensions
Expand All @@ -479,11 +479,11 @@ def care(A,B,Q,R=None,S=None,E=None):
m = 1
else:
m = size(B,1)
if R==None:
if R is None:
R = eye(m,m)

# Solve the standard algebraic Riccati equation
if S==None and E==None:
if S is None and E is None:
# Check input data for consistency
if size(A) > 1 and shape(A)[0] != shape(A)[1]:
raise ControlArgument("A must be a quadratic matrix.")
Expand Down Expand Up @@ -564,7 +564,7 @@ def care(A,B,Q,R=None,S=None,E=None):
return (X , w[:n] , G )

# Solve the generalized algebraic Riccati equation
elif S != None and E != None:
elif S is not None and E is not None:
# Check input data for consistency
if size(A) > 1 and shape(A)[0] != shape(A)[1]:
raise ControlArgument("A must be a quadratic matrix.")
Expand Down Expand Up @@ -674,7 +674,7 @@ def dare(A,B,Q,R,S=None,E=None):
""" (X,L,G) = dare(A,B,Q,R) solves the discrete-time algebraic Riccati
equation

A^T X A - X - A^T X B (B^T X B + R)^-1 B^T X A + Q = 0
:math:`A^T X A - X - A^T X B (B^T X B + R)^{-1} B^T X A + Q = 0`

where A and Q are square matrices of the same dimension. Further, Q
is a symmetric matrix. The function returns the solution X, the gain
Expand All @@ -684,12 +684,11 @@ def dare(A,B,Q,R,S=None,E=None):
(X,L,G) = dare(A,B,Q,R,S,E) solves the generalized discrete-time algebraic
Riccati equation

A^T X A - E^T X E - (A^T X B + S) (B^T X B + R)^-1 (B^T X A + S^T) +
+ Q = 0
:math:`A^T X A - E^T X E - (A^T X B + S) (B^T X B + R)^{-1} (B^T X A + S^T) + Q = 0`

where A, Q and E are square matrices of the same dimension. Further, Q and
R are symmetric matrices. The function returns the solution X, the gain
matrix G = (B^T X B + R)^-1 (B^T X A + S^T) and the closed loop
matrix :math:`G = (B^T X B + R)^{-1} (B^T X A + S^T)` and the closed loop
eigenvalues L, i.e., the eigenvalues of A - B G , E.
"""
if S is not None or E is not None:
Expand Down Expand Up @@ -730,13 +729,13 @@ def dare_old(A,B,Q,R,S=None,E=None):
if len(shape(Q)) == 1:
Q = Q.reshape(1,Q.size)

if R != None and len(shape(R)) == 1:
if R is not None and len(shape(R)) == 1:
R = R.reshape(1,R.size)

if S != None and len(shape(S)) == 1:
if S is not None and len(shape(S)) == 1:
S = S.reshape(1,S.size)

if E != None and len(shape(E)) == 1:
if E is not None and len(shape(E)) == 1:
E = E.reshape(1,E.size)

# Determine main dimensions
Expand All @@ -751,7 +750,7 @@ def dare_old(A,B,Q,R,S=None,E=None):
m = size(B,1)

# Solve the standard algebraic Riccati equation
if S==None and E==None:
if S is None and E is None:
# Check input data for consistency
if size(A) > 1 and shape(A)[0] != shape(A)[1]:
raise ControlArgument("A must be a quadratic matrix.")
Expand Down Expand Up @@ -835,7 +834,7 @@ def dare_old(A,B,Q,R,S=None,E=None):
return (X , w[:n] , G)

# Solve the generalized algebraic Riccati equation
elif S != None and E != None:
elif S is not None and E is not None:
# Check input data for consistency
if size(A) > 1 and shape(A)[0] != shape(A)[1]:
raise ControlArgument("A must be a quadratic matrix.")
Expand Down
Loading