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 + pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy