From 37b06f441c4e821234fcf07d8a778f1707e845bc Mon Sep 17 00:00:00 2001 From: Jed Date: Fri, 17 Feb 2017 13:57:46 -0500 Subject: [PATCH 1/8] Added notebook for doing development work on IO-Cleanup. Added IO cleanup function to xferfcn module. --- control/xferfcn.py | 92 ++++++++------- notebooks/IO_Cleanup.ipynb | 231 +++++++++++++++++++++++++++++++++++++ 2 files changed, 280 insertions(+), 43 deletions(-) create mode 100644 notebooks/IO_Cleanup.ipynb diff --git a/control/xferfcn.py b/control/xferfcn.py index 1559fa047..5fd9d9107 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -10,6 +10,7 @@ # Python 3 compatibility (needs to go here) from __future__ import print_function from __future__ import division +from __future__ import absolute_import """Copyright (c) 2010 by California Institute of Technology All rights reserved. @@ -55,6 +56,13 @@ from numpy import angle, any, array, empty, finfo, insert, ndarray, ones, \ polyadd, polymul, polyval, roots, sort, sqrt, zeros, squeeze, exp, pi, \ where, delete, real, poly, poly1d + +from numpy import int, int8, int16, int32, int64 +from numpy import float, float16, float32, float64, float128 +from numpy import complex, complex64, complex128, complex256 + +from copy import deepcopy + import numpy as np from scipy.signal import lti, tf2zpk, zpk2tf, cont2discrete from copy import deepcopy @@ -96,7 +104,7 @@ def __init__(self, *args): where sys is a TransferFunction object (continuous or discrete). """ - + args = deepcopy(args) if len(args) == 2: # The user provided a numerator and a denominator. (num, den) = args @@ -120,48 +128,8 @@ def __init__(self, *args): raise ValueError("Needs 1, 2 or 3 arguments; received %i." % len(args)) - # Make num and den into lists of lists of arrays, if necessary. - # Beware: this is a shallow copy! This should be okay, - # but be careful. - data = [num, den] - for i in range(len(data)): - # Check for a scalar (including 0d ndarray) - if (isinstance(data[i], (int, float, complex)) or - (isinstance(data[i], ndarray) and data[i].ndim == 0)): - # Convert scalar to list of list of array. - if (isinstance(data[i], int)): - # Convert integers to floats at this point - data[i] = [[array([data[i]], dtype=float)]] - else: - data[i] = [[array([data[i]])]] - elif (isinstance(data[i], (list, tuple, ndarray)) and - isinstance(data[i][0], (int, float, complex))): - # Convert array to list of list of array. - if (isinstance(data[i][0], int)): - # Convert integers to floats at this point - #! Not sure this covers all cases correctly - data[i] = [[array(data[i], dtype=float)]] - else: - data[i] = [[array(data[i])]] - elif (isinstance(data[i], list) and - isinstance(data[i][0], list) and - isinstance(data[i][0][0], (list, tuple, ndarray)) and - isinstance(data[i][0][0][0], (int, float, complex))): - # We might already have the right format. Convert the - # coefficient vectors to arrays, if necessary. - for j in range(len(data[i])): - for k in range(len(data[i][j])): - if (isinstance(data[i][j][k], int)): - data[i][j][k] = array(data[i][j][k], dtype=float) - else: - data[i][j][k] = array(data[i][j][k]) - else: - # If the user passed in anything else, then it's unclear what - # the meaning is. - raise TypeError("The numerator and denominator inputs must be \ -scalars or vectors (for\nSISO), or lists of lists of vectors (for SISO or \ -MIMO).") - [num, den] = data + num = tf_clean_parts(num) + den = tf_clean_parts(den) inputs = len(num[0]) outputs = len(num) @@ -1349,3 +1317,41 @@ def tfdata(sys): tf = _convertToTransferFunction(sys) return (tf.num, tf.den) + +def tf_clean_parts(data): + ''' + Return a valid, cleaned up numerator or denominator + for the TransferFunction class. + + Parameters: + data: numerator or denominator of a transfer function. + + Returns: + data: correctly formatted transfer function part. + + ''' + valid_types = (int, int8, int16, int32, int64, + float, float16, float32, float64, float128, + complex, complex64, complex128, complex256) + valid_collection = (list, tuple, ndarray) + + if (isinstance(data, valid_types) or + (isinstance(data, ndarray) and data.ndim == 0)): + return [[array([data], dtype=float)]] + elif (isinstance(data, valid_collection) and + all([isinstance(d, valid_types) for d in data])): + return [[array(data, dtype=float)]] + elif (isinstance(data, list) and + isinstance(data[0], list) and + (isinstance(data[0][0], valid_collection) and + isinstance(data[0][0][0], valid_types))): + for j in range(len(data)): + for k in range(len(data[j])): + data[j][k] = array(data[j][k], dtype=float) + return data + else: + # If the user passed in anything else, then it's unclear what + # the meaning is. + raise TypeError("The numerator and denominator inputs must be \ +scalars or vectors (for\nSISO), or lists of lists of vectors (for SISO or \ +MIMO).") \ No newline at end of file diff --git a/notebooks/IO_Cleanup.ipynb b/notebooks/IO_Cleanup.ipynb new file mode 100644 index 000000000..4aad9b79c --- /dev/null +++ b/notebooks/IO_Cleanup.ipynb @@ -0,0 +1,231 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from numpy import int, int8, int16, int32, int64\n", + "from numpy import float, float16, float32, float64, float128\n", + "from numpy import complex, complex64, complex128, complex256\n", + "from numpy import all, ndarray, array\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def tf_clean_parts(data):\n", + " '''\n", + " Return a valid, cleaned up numerator or denominator \n", + " for the TransferFunction class.\n", + " \n", + " Parameters:\n", + " data: numerator or denominator of a transfer function.\n", + " \n", + " Returns:\n", + " data: correctly formatted transfer function part.\n", + " \n", + " '''\n", + " valid_types = (int, int8, int16, int32, int64,\n", + " float, float16, float32, float64, float128,\n", + " complex, complex64, complex128, complex256)\n", + " valid_collection = (list, tuple, ndarray)\n", + "\n", + " if (isinstance(data, valid_types) or\n", + " (isinstance(data, ndarray) and data.ndim == 0)):\n", + " return [[array([data], dtype=float)]]\n", + " elif (isinstance(data, valid_collection) and\n", + " all([isinstance(d, valid_types) for d in data])):\n", + " return [[array(data, dtype=float)]]\n", + " elif (isinstance(data, list) and\n", + " isinstance(data[0], list) and\n", + " (isinstance(data[0][0], valid_collection) and \n", + " isinstance(data[0][0][0], valid_types))):\n", + " for j in range(len(data)):\n", + " for k in range(len(data[j])):\n", + " data[j][k] = array(data[j][k], dtype=float)\n", + " return data\n", + " else:\n", + " # If the user passed in anything else, then it's unclear what\n", + " # the meaning is.\n", + " raise TypeError(\"The numerator and denominator inputs must be \\\n", + "scalars or vectors (for\\nSISO), or lists of lists of vectors (for SISO or \\\n", + "MIMO).\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "num = 1\n", + "num_ = tf_clean_parts(num)\n", + "np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "num = [1]\n", + "num_ = tf_clean_parts(num)\n", + "np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [], + "source": [ + "num = [1,1]\n", + "num_ = tf_clean_parts(num)\n", + "np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "num = [[[1,1],[2,2]]]\n", + "num_ = tf_clean_parts(num)\n", + "np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float))\n", + "np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "num = [[[1.0,1.0],[2.0,2.0]]]\n", + "num_ = tf_clean_parts(num)\n", + "np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float))\n", + "np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "num = [[array([1,1]),array([2,2])]]\n", + "num_ = tf_clean_parts(num)\n", + "np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float))\n", + "np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "num = [[array([1.0,1.0]),array([2.0,2.0])]]\n", + "num_ = tf_clean_parts(num)\n", + "np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float))\n", + "np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[array([ 1., 2., 3.]), array([ 1., 2., 3.])]]" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "num = [[[1,2,3],[1,2,3]]]\n", + "tf_clean_parts(num)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "The numerator and denominator inputs must be scalars or vectors (for\nSISO), or lists of lists of vectors (for SISO or MIMO).", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mnum\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mtf_clean_parts\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mtf_clean_parts\u001b[0;34m(data)\u001b[0m\n\u001b[1;32m 33\u001b[0m \u001b[0;31m# If the user passed in anything else, then it's unclear what\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 34\u001b[0m \u001b[0;31m# the meaning is.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 35\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"The numerator and denominator inputs must be scalars or vectors (for\\nSISO), or lists of lists of vectors (for SISO or MIMO).\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: The numerator and denominator inputs must be scalars or vectors (for\nSISO), or lists of lists of vectors (for SISO or MIMO)." + ] + } + ], + "source": [ + "num = [[1,2,3],[1,2,3]]\n", + "tf_clean_parts(num)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} From feb68f5266e046b1a425b65c19780157c42454d7 Mon Sep 17 00:00:00 2001 From: Jed Date: Fri, 17 Feb 2017 13:58:46 -0500 Subject: [PATCH 2/8] Added .eggs directory to gitignore. From .eggs/README.txt: "This directory contains eggs that were downloaded by setuptools to build, test, and run plug-ins. This directory caches those eggs to prevent repeated downloads. However, it is safe to delete this directory." --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c63a6cf06..ff6a58c0a 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ examples/.ipynb_checkpoints/ .project Untitled*.ipynb *.idea/ +.eggs From 5e2d95d116454338f602d06a955af94abb1029fc Mon Sep 17 00:00:00 2001 From: Jed Date: Sat, 18 Feb 2017 11:23:47 -0500 Subject: [PATCH 3/8] Broke out the numerator/denominator cleanup function into a separate function. Added unit tests to test the cleanup function. Added deep copy to TransferFunction args. --- control/tests/xferfcn_input_test.py | 261 ++++++++++++++++++++++++++++ control/xferfcn.py | 19 +- 2 files changed, 271 insertions(+), 9 deletions(-) create mode 100644 control/tests/xferfcn_input_test.py diff --git a/control/tests/xferfcn_input_test.py b/control/tests/xferfcn_input_test.py new file mode 100644 index 000000000..d66df29a1 --- /dev/null +++ b/control/tests/xferfcn_input_test.py @@ -0,0 +1,261 @@ +#!/usr/bin/env python +# +# xferfcn_test.py - test TransferFunction class +# RMM, 30 Mar 2011 (based on TestXferFcn from v0.4a) + +import unittest +import numpy as np + +from numpy import int, int8, int16, int32, int64 +from numpy import float, float16, float32, float64, float128 +from numpy import all, ndarray, array + +from control.xferfcn import cleanPart + +class TestXferFcnInput(unittest.TestCase): + """These are tests for functionality of cleaning and validating + XferFucnInput.""" + + # Tests for raising exceptions. + def testBadInputType(self): + """Give the part cleaner invalid input type.""" + + self.assertRaises(TypeError, cleanPart, [[0., 1.], [2., 3.]]) + + def testBadInputType2(self): + """Give the part cleaner another invalid input type.""" + self.assertRaises(TypeError, cleanPart, [1,"a"]) + + def testScalar(self): + """Test single scalar value.""" + num = 1 + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float)) + + def testListScalar(self): + """Test single scalar value in list.""" + num = [1] + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float)) + + def testTupleScalar(self): + """Test single scalar value in tuple.""" + num = (1) + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float)) + + def testList(self): + """Test multiple values in a list.""" + num = [1, 2] + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0, 2.0], dtype=float)) + + def testTuple(self): + """Test multiple values in tuple.""" + num = (1, 2) + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0, 2.0], dtype=float)) + + def testAllScalarTypes(self): + """Test single scalar value for all valid data types.""" + for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: + num = dtype(1) + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float)) + + def testNpArray(self): + """Test multiple values in numpy array.""" + num = np.array([1, 2]) + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0, 2.0], dtype=float)) + + def testAllNumpyArrayTypes(self): + """Test scalar value in numpy array of ndim=0 for all data types.""" + for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: + num = np.array(1, dtype=dtype) + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float)) + + def testAllNumpyArrayTypes2(self): + """Test numpy array for all types.""" + for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: + num = np.array([1, 2], dtype=dtype) + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0, 2.0], dtype=float)) + + def testListAllTypes(self): + """Test list of a single value for all data types.""" + for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: + num = [dtype(1)] + num_ = cleanPart(num) + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float)) + + def testListAllTypes2(self): + """List of list of numbers of all data types.""" + for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: + num = [dtype(1), dtype(2)] + num_ = cleanPart(num) + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0, 2.0], dtype=float)) + + def testTupleAllTypes(self): + """Test tuple of a single value for all data types.""" + for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: + num = (dtype(1),) + num_ = cleanPart(num) + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float)) + + def testTupleAllTypes2(self): + """Test tuple of a single value for all data types.""" + for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: + num = (dtype(1), dtype(2)) + num_ = cleanPart(num) + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1, 2], dtype=float)) + + def testListListListInt(self): + """ Test an int in a list of a list of a list.""" + num = [[[1]]] + num_ = cleanPart(num) + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float)) + + def testListListListFloat(self): + """ Test a float in a list of a list of a list.""" + num = [[[1.0]]] + num_ = cleanPart(num) + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float)) + + def testListListListInts(self): + """Test 2 lists of ints in a list in a list.""" + num = [[[1,1],[2,2]]] + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float)) + np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float)) + + def testListListListFloats(self): + """Test 2 lists of ints in a list in a list.""" + num = [[[1.0,1.0],[2.0,2.0]]] + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float)) + np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float)) + + def testListListArray(self): + """List of list of numpy arrays for all valid types.""" + for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: + num = [[array([1,1], dtype=dtype),array([2,2], dtype=dtype)]] + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float)) + np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float)) + + def testTupleListArray(self): + """Tuple of list of numpy arrays for all valid types.""" + for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: + num = ([array([1,1], dtype=dtype),array([2,2], dtype=dtype)],) + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float)) + np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float)) + + def testListTupleArray(self): + """List of tuple of numpy array for all valid types.""" + for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: + num = [(array([1,1], dtype=dtype),array([2,2], dtype=dtype))] + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float)) + np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float)) + + def testTupleTuplesArrays(self): + """Tuple of tuples of numpy arrays for all valid types.""" + for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: + num = ((array([1,1], dtype=dtype),array([2,2], dtype=dtype)), + (array([3,4], dtype=dtype),array([4,4], dtype=dtype))) + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float)) + np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float)) + + def testListTuplesArrays(self): + """List of tuples of numpy arrays for all valid types.""" + for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: + num = [(array([1,1], dtype=dtype),array([2,2], dtype=dtype)), + (array([3,4], dtype=dtype),array([4,4], dtype=dtype))] + num_ = cleanPart(num) + + assert isinstance(num_, list) + assert np.all([isinstance(part, list) for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float)) + np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float)) + + def testListListArrays(self): + """List of list of numpy arrays for all valid types.""" + for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: + num = [[array([1,1], dtype=dtype),array([2,2], dtype=dtype)], + [array([3,3], dtype=dtype),array([4,4], dtype=dtype)]] + num_ = cleanPart(num) + + assert len(num_) == 2 + assert np.all([isinstance(part, list) for part in num_]) + assert np.all([len(part) == 2 for part in num_]) + np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float)) + np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float)) + np.testing.assert_array_equal(num_[1][0], array([3.0, 3.0], dtype=float)) + np.testing.assert_array_equal(num_[1][1], array([4.0, 4.0], dtype=float)) + +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(TestXferFcnInput) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/control/xferfcn.py b/control/xferfcn.py index 5fd9d9107..f5c3475a7 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -128,8 +128,8 @@ def __init__(self, *args): raise ValueError("Needs 1, 2 or 3 arguments; received %i." % len(args)) - num = tf_clean_parts(num) - den = tf_clean_parts(den) + num = cleanPart(num) + den = cleanPart(den) inputs = len(num[0]) outputs = len(num) @@ -1318,7 +1318,7 @@ def tfdata(sys): return (tf.num, tf.den) -def tf_clean_parts(data): +def cleanPart(data): ''' Return a valid, cleaned up numerator or denominator for the TransferFunction class. @@ -1328,11 +1328,10 @@ def tf_clean_parts(data): Returns: data: correctly formatted transfer function part. - + ; ''' valid_types = (int, int8, int16, int32, int64, - float, float16, float32, float64, float128, - complex, complex64, complex128, complex256) + float, float16, float32, float64, float128) valid_collection = (list, tuple, ndarray) if (isinstance(data, valid_types) or @@ -1341,11 +1340,13 @@ def tf_clean_parts(data): elif (isinstance(data, valid_collection) and all([isinstance(d, valid_types) for d in data])): return [[array(data, dtype=float)]] - elif (isinstance(data, list) and - isinstance(data[0], list) and + elif (isinstance(data, (list, tuple)) and + isinstance(data[0], (list, tuple)) and (isinstance(data[0][0], valid_collection) and - isinstance(data[0][0][0], valid_types))): + all([isinstance(d, valid_types) for d in data[0][0]]))): + data = list(data) for j in range(len(data)): + data[j] = list(data[j]) for k in range(len(data[j])): data[j][k] = array(data[j][k], dtype=float) return data From 509ff8e849f27539016b15e41df6e8e61acd6a29 Mon Sep 17 00:00:00 2001 From: Jed Date: Sat, 18 Feb 2017 11:28:44 -0500 Subject: [PATCH 4/8] Removed test notebook --- notebooks/IO_Cleanup.ipynb | 231 ------------------------------------- 1 file changed, 231 deletions(-) delete mode 100644 notebooks/IO_Cleanup.ipynb diff --git a/notebooks/IO_Cleanup.ipynb b/notebooks/IO_Cleanup.ipynb deleted file mode 100644 index 4aad9b79c..000000000 --- a/notebooks/IO_Cleanup.ipynb +++ /dev/null @@ -1,231 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from numpy import int, int8, int16, int32, int64\n", - "from numpy import float, float16, float32, float64, float128\n", - "from numpy import complex, complex64, complex128, complex256\n", - "from numpy import all, ndarray, array\n", - "import numpy as np" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "def tf_clean_parts(data):\n", - " '''\n", - " Return a valid, cleaned up numerator or denominator \n", - " for the TransferFunction class.\n", - " \n", - " Parameters:\n", - " data: numerator or denominator of a transfer function.\n", - " \n", - " Returns:\n", - " data: correctly formatted transfer function part.\n", - " \n", - " '''\n", - " valid_types = (int, int8, int16, int32, int64,\n", - " float, float16, float32, float64, float128,\n", - " complex, complex64, complex128, complex256)\n", - " valid_collection = (list, tuple, ndarray)\n", - "\n", - " if (isinstance(data, valid_types) or\n", - " (isinstance(data, ndarray) and data.ndim == 0)):\n", - " return [[array([data], dtype=float)]]\n", - " elif (isinstance(data, valid_collection) and\n", - " all([isinstance(d, valid_types) for d in data])):\n", - " return [[array(data, dtype=float)]]\n", - " elif (isinstance(data, list) and\n", - " isinstance(data[0], list) and\n", - " (isinstance(data[0][0], valid_collection) and \n", - " isinstance(data[0][0][0], valid_types))):\n", - " for j in range(len(data)):\n", - " for k in range(len(data[j])):\n", - " data[j][k] = array(data[j][k], dtype=float)\n", - " return data\n", - " else:\n", - " # If the user passed in anything else, then it's unclear what\n", - " # the meaning is.\n", - " raise TypeError(\"The numerator and denominator inputs must be \\\n", - "scalars or vectors (for\\nSISO), or lists of lists of vectors (for SISO or \\\n", - "MIMO).\")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "num = 1\n", - "num_ = tf_clean_parts(num)\n", - "np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float))" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "num = [1]\n", - "num_ = tf_clean_parts(num)\n", - "np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float))" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": false, - "scrolled": true - }, - "outputs": [], - "source": [ - "num = [1,1]\n", - "num_ = tf_clean_parts(num)\n", - "np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float))" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "num = [[[1,1],[2,2]]]\n", - "num_ = tf_clean_parts(num)\n", - "np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float))\n", - "np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float))" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "num = [[[1.0,1.0],[2.0,2.0]]]\n", - "num_ = tf_clean_parts(num)\n", - "np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float))\n", - "np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float))" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "num = [[array([1,1]),array([2,2])]]\n", - "num_ = tf_clean_parts(num)\n", - "np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float))\n", - "np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float))" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "num = [[array([1.0,1.0]),array([2.0,2.0])]]\n", - "num_ = tf_clean_parts(num)\n", - "np.testing.assert_array_equal(num_[0][0], array([1.0, 1.0], dtype=float))\n", - "np.testing.assert_array_equal(num_[0][1], array([2.0, 2.0], dtype=float))" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[[array([ 1., 2., 3.]), array([ 1., 2., 3.])]]" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "num = [[[1,2,3],[1,2,3]]]\n", - "tf_clean_parts(num)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "ename": "TypeError", - "evalue": "The numerator and denominator inputs must be scalars or vectors (for\nSISO), or lists of lists of vectors (for SISO or MIMO).", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mnum\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mtf_clean_parts\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mtf_clean_parts\u001b[0;34m(data)\u001b[0m\n\u001b[1;32m 33\u001b[0m \u001b[0;31m# If the user passed in anything else, then it's unclear what\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 34\u001b[0m \u001b[0;31m# the meaning is.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 35\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"The numerator and denominator inputs must be scalars or vectors (for\\nSISO), or lists of lists of vectors (for SISO or MIMO).\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m: The numerator and denominator inputs must be scalars or vectors (for\nSISO), or lists of lists of vectors (for SISO or MIMO)." - ] - } - ], - "source": [ - "num = [[1,2,3],[1,2,3]]\n", - "tf_clean_parts(num)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.5.2" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} From d44489f1b0636a1010ad50f4a13a8723254f7b7b Mon Sep 17 00:00:00 2001 From: Jed Date: Sat, 18 Feb 2017 14:11:00 -0500 Subject: [PATCH 5/8] Renamed cleanPart to _cleanPart since it is an internal function, as suggested by @murrayrm --- control/tests/xferfcn_input_test.py | 54 ++++++++++++++--------------- control/xferfcn.py | 8 ++--- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/control/tests/xferfcn_input_test.py b/control/tests/xferfcn_input_test.py index d66df29a1..cb9da4427 100644 --- a/control/tests/xferfcn_input_test.py +++ b/control/tests/xferfcn_input_test.py @@ -10,7 +10,7 @@ from numpy import float, float16, float32, float64, float128 from numpy import all, ndarray, array -from control.xferfcn import cleanPart +from control.xferfcn import _cleanPart class TestXferFcnInput(unittest.TestCase): """These are tests for functionality of cleaning and validating @@ -20,16 +20,16 @@ class TestXferFcnInput(unittest.TestCase): def testBadInputType(self): """Give the part cleaner invalid input type.""" - self.assertRaises(TypeError, cleanPart, [[0., 1.], [2., 3.]]) + self.assertRaises(TypeError, _cleanPart, [[0., 1.], [2., 3.]]) def testBadInputType2(self): """Give the part cleaner another invalid input type.""" - self.assertRaises(TypeError, cleanPart, [1,"a"]) + self.assertRaises(TypeError, _cleanPart, [1,"a"]) def testScalar(self): """Test single scalar value.""" num = 1 - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -38,7 +38,7 @@ def testScalar(self): def testListScalar(self): """Test single scalar value in list.""" num = [1] - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -47,7 +47,7 @@ def testListScalar(self): def testTupleScalar(self): """Test single scalar value in tuple.""" num = (1) - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -56,7 +56,7 @@ def testTupleScalar(self): def testList(self): """Test multiple values in a list.""" num = [1, 2] - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -65,7 +65,7 @@ def testList(self): def testTuple(self): """Test multiple values in tuple.""" num = (1, 2) - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -75,7 +75,7 @@ def testAllScalarTypes(self): """Test single scalar value for all valid data types.""" for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: num = dtype(1) - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -84,7 +84,7 @@ def testAllScalarTypes(self): def testNpArray(self): """Test multiple values in numpy array.""" num = np.array([1, 2]) - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -94,7 +94,7 @@ def testAllNumpyArrayTypes(self): """Test scalar value in numpy array of ndim=0 for all data types.""" for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: num = np.array(1, dtype=dtype) - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -104,7 +104,7 @@ def testAllNumpyArrayTypes2(self): """Test numpy array for all types.""" for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: num = np.array([1, 2], dtype=dtype) - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -114,7 +114,7 @@ def testListAllTypes(self): """Test list of a single value for all data types.""" for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: num = [dtype(1)] - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float)) @@ -123,7 +123,7 @@ def testListAllTypes2(self): """List of list of numbers of all data types.""" for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: num = [dtype(1), dtype(2)] - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) np.testing.assert_array_equal(num_[0][0], array([1.0, 2.0], dtype=float)) @@ -132,7 +132,7 @@ def testTupleAllTypes(self): """Test tuple of a single value for all data types.""" for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: num = (dtype(1),) - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float)) @@ -141,7 +141,7 @@ def testTupleAllTypes2(self): """Test tuple of a single value for all data types.""" for dtype in [int, int8, int16, int32, int64, float, float16, float32, float64, float128]: num = (dtype(1), dtype(2)) - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) np.testing.assert_array_equal(num_[0][0], array([1, 2], dtype=float)) @@ -149,7 +149,7 @@ def testTupleAllTypes2(self): def testListListListInt(self): """ Test an int in a list of a list of a list.""" num = [[[1]]] - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float)) @@ -157,7 +157,7 @@ def testListListListInt(self): def testListListListFloat(self): """ Test a float in a list of a list of a list.""" num = [[[1.0]]] - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) np.testing.assert_array_equal(num_[0][0], array([1.0], dtype=float)) @@ -165,7 +165,7 @@ def testListListListFloat(self): def testListListListInts(self): """Test 2 lists of ints in a list in a list.""" num = [[[1,1],[2,2]]] - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -175,7 +175,7 @@ def testListListListInts(self): def testListListListFloats(self): """Test 2 lists of ints in a list in a list.""" num = [[[1.0,1.0],[2.0,2.0]]] - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -186,7 +186,7 @@ def testListListArray(self): """List of list of numpy arrays for all valid types.""" for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: num = [[array([1,1], dtype=dtype),array([2,2], dtype=dtype)]] - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -197,7 +197,7 @@ def testTupleListArray(self): """Tuple of list of numpy arrays for all valid types.""" for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: num = ([array([1,1], dtype=dtype),array([2,2], dtype=dtype)],) - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -208,7 +208,7 @@ def testListTupleArray(self): """List of tuple of numpy array for all valid types.""" for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: num = [(array([1,1], dtype=dtype),array([2,2], dtype=dtype))] - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -220,7 +220,7 @@ def testTupleTuplesArrays(self): for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: num = ((array([1,1], dtype=dtype),array([2,2], dtype=dtype)), (array([3,4], dtype=dtype),array([4,4], dtype=dtype))) - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -232,7 +232,7 @@ def testListTuplesArrays(self): for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: num = [(array([1,1], dtype=dtype),array([2,2], dtype=dtype)), (array([3,4], dtype=dtype),array([4,4], dtype=dtype))] - num_ = cleanPart(num) + num_ = _cleanPart(num) assert isinstance(num_, list) assert np.all([isinstance(part, list) for part in num_]) @@ -244,7 +244,7 @@ def testListListArrays(self): for dtype in int, int8, int16, int32, int64, float, float16, float32, float64, float128: num = [[array([1,1], dtype=dtype),array([2,2], dtype=dtype)], [array([3,3], dtype=dtype),array([4,4], dtype=dtype)]] - num_ = cleanPart(num) + num_ = _cleanPart(num) assert len(num_) == 2 assert np.all([isinstance(part, list) for part in num_]) @@ -258,4 +258,4 @@ def suite(): return unittest.TestLoader().loadTestsFromTestCase(TestXferFcnInput) if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() diff --git a/control/xferfcn.py b/control/xferfcn.py index f5c3475a7..df384ad74 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -128,8 +128,8 @@ def __init__(self, *args): raise ValueError("Needs 1, 2 or 3 arguments; received %i." % len(args)) - num = cleanPart(num) - den = cleanPart(den) + num = _cleanPart(num) + den = _cleanPart(den) inputs = len(num[0]) outputs = len(num) @@ -1318,7 +1318,7 @@ def tfdata(sys): return (tf.num, tf.den) -def cleanPart(data): +def _cleanPart(data): ''' Return a valid, cleaned up numerator or denominator for the TransferFunction class. @@ -1355,4 +1355,4 @@ def cleanPart(data): # the meaning is. raise TypeError("The numerator and denominator inputs must be \ scalars or vectors (for\nSISO), or lists of lists of vectors (for SISO or \ -MIMO).") \ No newline at end of file +MIMO).") From b1437f0e025fa732feb9653ff322c1e80b157d5f Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Fri, 29 Dec 2017 08:17:46 -0800 Subject: [PATCH 6/8] unit test from kangwonlee commit facfe9c49d79227dd3bdcd22af9d0aa917ac6008 --- control/tests/tf_input_element_int.py | 46 +++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 control/tests/tf_input_element_int.py diff --git a/control/tests/tf_input_element_int.py b/control/tests/tf_input_element_int.py new file mode 100644 index 000000000..13d0310e7 --- /dev/null +++ b/control/tests/tf_input_element_int.py @@ -0,0 +1,46 @@ +import unittest + +import numpy as np + +import control as ctl + + +class TestTfInputIntElement(unittest.TestCase): + # currently these do not pass + def test_tf_den_with_numpy_int_element(self): + num = 1 + den = np.convolve([1, 2, 1], [1, 1, 1]) + + sys = ctl.tf(num, den) + + self.assertAlmostEqual(1.0, ctl.dcgain(sys)) + + def test_tf_num_with_numpy_int_element(self): + num = np.convolve([1], [1, 1]) + den = np.convolve([1, 2, 1], [1, 1, 1]) + + sys = ctl.tf(num, den) + + self.assertAlmostEqual(1.0, ctl.dcgain(sys)) + + # currently these pass + def test_tf_input_with_int_element_works(self): + num = 1 + den = np.convolve([1.0, 2, 1], [1, 1, 1]) + + sys = ctl.tf(num, den) + + self.assertAlmostEqual(1.0, ctl.dcgain(sys)) + + def test_ss_input_with_int_element(self): + ident = np.matrix(np.identity(2), dtype=int) + a = np.matrix([[0, 1], + [-1, -2]], dtype=int) * ident + b = np.matrix([[0], + [1]], dtype=int) + c = np.matrix([[0, 1]], dtype=int) + d = 0 + + sys = ctl.ss(a, b, c, d) + sys2 = ctl.ss2tf(sys) + self.assertAlmostEqual(ctl.dcgain(sys), ctl.dcgain(sys2)) From 10cdb7a6bbfa0b9ed9cdff9fac9ea74046003b4d Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Fri, 29 Dec 2017 08:51:00 -0800 Subject: [PATCH 7/8] TST: renamed unit test from kangwonlee --- ...ut_element_int.py => input_element_int_test.py} | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) rename control/tests/{tf_input_element_int.py => input_element_int_test.py} (81%) diff --git a/control/tests/tf_input_element_int.py b/control/tests/input_element_int_test.py similarity index 81% rename from control/tests/tf_input_element_int.py rename to control/tests/input_element_int_test.py index 13d0310e7..c6a6f64a3 100644 --- a/control/tests/tf_input_element_int.py +++ b/control/tests/input_element_int_test.py @@ -1,10 +1,18 @@ -import unittest +# input_element_int_test.py +# +# Author: Kangwon Lee (kangwonlee) +# Date: 22 Oct 2017 +# +# Unit tests contributed as part of PR #158, "SISO tf() may not work +# with numpy arrays with numpy.int elements" +# +# Modified: +# * 29 Dec 2017, RMM - updated file name and added header +import unittest import numpy as np - import control as ctl - class TestTfInputIntElement(unittest.TestCase): # currently these do not pass def test_tf_den_with_numpy_int_element(self): From 68b5dd153d67388b448d96e25edb618aa2864c41 Mon Sep 17 00:00:00 2001 From: Richard Murray Date: Fri, 29 Dec 2017 14:56:42 -0800 Subject: [PATCH 8/8] address additional comments from roryyorke in PR #135 --- .gitignore | 6 +++++- control/tests/xferfcn_input_test.py | 4 ++-- control/xferfcn.py | 25 +++++++++++++------------ 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index ff6a58c0a..0262ab46f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ __conda_*.txt record.txt build.log *.egg-info/ +.eggs/ .coverage doc/_build doc/generated @@ -18,4 +19,7 @@ examples/.ipynb_checkpoints/ .project Untitled*.ipynb *.idea/ -.eggs + +# Files created by or for emacs (RMM, 29 Dec 2017) +*~ +TAGS diff --git a/control/tests/xferfcn_input_test.py b/control/tests/xferfcn_input_test.py index cb9da4427..acddc64ae 100644 --- a/control/tests/xferfcn_input_test.py +++ b/control/tests/xferfcn_input_test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -# xferfcn_test.py - test TransferFunction class -# RMM, 30 Mar 2011 (based on TestXferFcn from v0.4a) +# xferfcn_input_test.py - test inputs to TransferFunction class +# jed-frey, 18 Feb 2017 (based on xferfcn_test.py) import unittest import numpy as np diff --git a/control/xferfcn.py b/control/xferfcn.py index ebe35621f..982b8c5ba 100644 --- a/control/xferfcn.py +++ b/control/xferfcn.py @@ -10,7 +10,6 @@ # Python 3 compatibility (needs to go here) from __future__ import print_function from __future__ import division -from __future__ import absolute_import """Copyright (c) 2010 by California Institute of Technology All rights reserved. @@ -170,13 +169,6 @@ def __init__(self, *args): if zeronum: den[i][j] = ones(1) - # Check for coefficients that are ints and convert to floats - # TODO - for k in range(den[i][j]): - if (isinstance(data[i], (int, np.int))): - den[i][j][k] = float(den[i][j][k]) - - LTI.__init__(self, inputs, outputs, dt) self.num = num self.den = den @@ -1338,7 +1330,7 @@ def _cleanPart(data): Returns ------- - data: correctly formatted transfer function part. + data: list of lists of ndarrays, with int converted to float ''' valid_types = (int, float, complex, np.number) valid_collection = (list, tuple, ndarray) @@ -1346,10 +1338,10 @@ def _cleanPart(data): if (isinstance(data, valid_types) or (isinstance(data, ndarray) and data.ndim == 0)): # Data is a scalar (including 0d ndarray) - return [[array([data])]] + data = [[array([data])]] elif (isinstance(data, valid_collection) and all([isinstance(d, valid_types) for d in data])): - return [[array(data]] + data = [[array(data)]] elif (isinstance(data, (list, tuple)) and isinstance(data[0], (list, tuple)) and (isinstance(data[0][0], valid_collection) and @@ -1359,10 +1351,19 @@ def _cleanPart(data): data[j] = list(data[j]) for k in range(len(data[j])): data[j][k] = array(data[j][k]) - return data else: # If the user passed in anything else, then it's unclear what # the meaning is. raise TypeError("The numerator and denominator inputs must be \ scalars or vectors (for\nSISO), or lists of lists of vectors (for SISO or \ MIMO).") + + # Check for coefficients that are ints and convert to floats + for i in range(len(data)): + for j in range(len(data[i])): + for k in range(len(data[i][j])): + if (isinstance(data[i][j][k], (int, np.int))): + data[i][j][k] = float(data[i][j][k]) + + return data +