Multiply scipy.lti transfer functions - python

I have the following scipy.lti object that is basically an object representing a Laplace transform of an LTI system:
G_s = lti([1], [1, 2])
How to multiply such a transfer function with another one, such as i.e.:
H_s = lti([2], [1, 2])
#I_s = G_s * H_s <---- How to multiply this properly?
I guess I could do
I_s = lti(np.polymul([1], [2]), np.polymul([1, 2], [1, 2]))
But what if I want to do:
#I_s = H_s / (1 + H_s) <---- Does not work since H_s is an lti object
Is there an easy way to do this with scipy?

Interestingly, Scipy does not seem to supply that functionality. An alternative is converting the LTI system into a Sympy rational function. Sympy allows you to easily expand and cancel polynomials:
from IPython.display import display
from scipy import signal
import sympy as sy
sy.init_printing() # LaTeX like pretty printing for IPython
def lti_to_sympy(lsys, symplify=True):
""" Convert Scipy's LTI instance to Sympy expression """
s = sy.Symbol('s')
G = sy.Poly(lsys.num, s) / sy.Poly(lsys.den, s)
return sy.simplify(G) if symplify else G
def sympy_to_lti(xpr, s=sy.Symbol('s')):
""" Convert Sympy transfer function polynomial to Scipy LTI """
num, den = sy.simplify(xpr).as_numer_denom() # expressions
p_num_den = sy.poly(num, s), sy.poly(den, s) # polynomials
c_num_den = [sy.expand(p).all_coeffs() for p in p_num_den] # coefficients
l_num, l_den = [sy.lambdify((), c)() for c in c_num_den] # convert to floats
return signal.lti(l_num, l_den)
pG, pH, pGH, pIGH = sy.symbols("G, H, GH, IGH") # only needed for displaying
# Sample systems:
lti_G = signal.lti([1], [1, 2])
lti_H = signal.lti([2], [1, 0, 3])
# convert to Sympy:
Gs, Hs = lti_to_sympy(lti_G), lti_to_sympy(lti_H)
print("Converted LTI expressions:")
display(sy.Eq(pG, Gs))
display(sy.Eq(pH, Hs))
print("Multiplying Systems:")
GHs = sy.simplify(Gs*Hs).expand() # make sure polynomials are canceled and expanded
display(sy.Eq(pGH, GHs))
print("Closing the loop:")
IGHs = sy.simplify(GHs / (1+GHs)).expand()
display(sy.Eq(pIGH, IGHs))
print("Back to LTI:")
lti_IGH = sympy_to_lti(IGHs)
print(lti_IGH)
The output is:

Depending on your definition of "easy", you should consider deriving your own class from lti, implementing the necessary algebraic operations on your transfer functions. This is probably the most elegant approach.
Here's my take on the subject:
from __future__ import division
from scipy.signal.ltisys import TransferFunction as TransFun
from numpy import polymul,polyadd
class ltimul(TransFun):
def __neg__(self):
return ltimul(-self.num,self.den)
def __floordiv__(self,other):
# can't make sense of integer division right now
return NotImplemented
def __mul__(self,other):
if type(other) in [int, float]:
return ltimul(self.num*other,self.den)
elif type(other) in [TransFun, ltimul]:
numer = polymul(self.num,other.num)
denom = polymul(self.den,other.den)
return ltimul(numer,denom)
def __truediv__(self,other):
if type(other) in [int, float]:
return ltimul(self.num,self.den*other)
if type(other) in [TransFun, ltimul]:
numer = polymul(self.num,other.den)
denom = polymul(self.den,other.num)
return ltimul(numer,denom)
def __rtruediv__(self,other):
if type(other) in [int, float]:
return ltimul(other*self.den,self.num)
if type(other) in [TransFun, ltimul]:
numer = polymul(self.den,other.num)
denom = polymul(self.num,other.den)
return ltimul(numer,denom)
def __add__(self,other):
if type(other) in [int, float]:
return ltimul(polyadd(self.num,self.den*other),self.den)
if type(other) in [TransFun, type(self)]:
numer = polyadd(polymul(self.num,other.den),polymul(self.den,other.num))
denom = polymul(self.den,other.den)
return ltimul(numer,denom)
def __sub__(self,other):
if type(other) in [int, float]:
return ltimul(polyadd(self.num,-self.den*other),self.den)
if type(other) in [TransFun, type(self)]:
numer = polyadd(polymul(self.num,other.den),-polymul(self.den,other.num))
denom = polymul(self.den,other.den)
return ltimul(numer,denom)
def __rsub__(self,other):
if type(other) in [int, float]:
return ltimul(polyadd(-self.num,self.den*other),self.den)
if type(other) in [TransFun, type(self)]:
numer = polyadd(polymul(other.num,self.den),-polymul(other.den,self.num))
denom = polymul(self.den,other.den)
return ltimul(numer,denom)
# sheer laziness: symmetric behaviour for commutative operators
__rmul__ = __mul__
__radd__ = __add__
This defines the ltimul class, which is lti plus addition, multiplication, division, subtraction, and negation; binary ones also defined for integers and floats as partners.
I tested it for the example of Dietrich:
G_s = ltimul([1], [1, 2])
H_s = ltimul([2], [1, 0, 3])
print(G_s*H_s)
print(G_s*H_s/(1+G_s*H_s))
While GH is nicely equal to
ltimul(
array([ 2.]),
array([ 1., 2., 3., 6.])
)
the final result for GH/(1+GH) is less pretty:
ltimul(
array([ 2., 4., 6., 12.]),
array([ 1., 4., 10., 26., 37., 42., 48.])
)
Since I'm not very familiar with transfer functions, I'm not sure how likely it is that this gives the same result as the sympy-based solution due to some simplifications missing from this one. I find it suspicious that already lti behaves unexpectedly: lti([1,2],[1,2]) doesn't simplify its arguments, even though I'd suspect this function to be constant 1. So I'd rather not guess the correctness of this final result.
Anyway, the main message is inheritance itself, so possible bugs in the above implementation hopefully pose only a minor inconvenience. I'm also quite unfamiliar with class definitions, so it's possible that I didn't follow best practices in the above.
I eventually rewrote the above after #ochurlaud pointed out, that my original only worked for Python 2. The reason is that the / operation is implemented by __div__/__rdiv__ in Python 2 (and is the ambiguous "classical division"). In Python 3, however, there is a distinction between / (true division) and // (floor division), and they call __truediv__ and __floordiv__ (and their "right" counterparts), respectively. The __future__ import first in the line of the above code triggers the proper Python 3 behaviour even on Python 2, so the above works on both Python versions. Since floor (integer) division doesn't make much sense for our class, we explicitly signal that it can't do anything with // (unless the other operand implements it).
One could also easily define the respective __iadd__, __idiv__ etc. in-place operations for +=, /= etc., respectively.

Related

Binary operations between two 0-dim ndarrays don't preserve type

The docs about ndarray state the following:
Arithmetic and comparison operations on ndarrays are defined as element-wise operations, and generally yield ndarray objects as results.
This makes sense as it would be very surprising if type(x) == type(y) but type(x {op} y) != type(x) (where {op} stands for an arbitrary binary operator).
However, np.ndarray seems to behave that way if ndim == 0 for both operands:
>>> x = np.array(1.)
>>> f'{x = }, {x.ndim = }'
'x = array(1.), x.ndim = 0'
>>> f'{x+x = }, {type(x+x) = }'
"x+x = 2.0, type(x+x) = <class 'numpy.float64'>"
This is inconvenient because my code assumes that binary operations do preserve the type (which is reasonable). Also, static type checking won't capture that:
from typing import TypeVar
import numpy as np
T = TypeVar('T', bound=np.ndarray)
def add(x: T, y: T) -> T:
return x + y
a = np.array(1.)
assert isinstance(add(a, a), np.ndarray)
According to mypy this snippet contains no issues but the assertion fails when run.
Is there a way to configure Numpy such that it will preserve the ndarray type even for 0-dim arrays?

Sympy won't simplify or expand exponential with decimals

i'm trying to simplify a huge expression of powers of n , and one of the results of sympy throws a (n+1)^1.0 , i noticed that
f=n*((n+1)**1.0)
sympy.expand(f)
doesn't work it stays the same instead of giving n^2+n, so i was wondering if there's any way to perform something like this
Sympy will expand your expression as expected when the power is an integer number. If the power is stored as a rational or a float, it won't work. Your options are either to rewrite your expression using integers, or write some code that will automatically check if a float stores an integer number (up to numerical precision error) and act accordingly.
Here's a starting point for that:
def rewrite_polynomial(p):
args_list = []
if not p.is_Mul:
return None
for m in p.args:
if not m.is_Pow:
args_list.append(m)
else:
pow_val = m.args[1]
if pow_val.is_Float:
pow_val_int = int(pow_val)
if pow_val.epsilon_eq(pow_val_int):
args_list.append(Pow(m.args[0],Integer(pow_val_int)))
else:
args_list.append(m)
else:
args_list.append(m)
return Mul(*args_list)
n = Symbol('n')
f= n*((n+1)**1.0)
g = rewrite_polynomial(f)
print(g)
Based on Yakovs answer, I made a rewrite rule that makes a DFS traversal of the expression tree and replaces powers to integers in float type.
The code is probably not very efficient, but it worked for my use cases.
Since I'm not a sympy expert, I guess there are some edge cases where this code will break.
Anyways, here you go!
import sympy as s
def recurse_replace(expr,pred,func):
if len(expr.args) == 0:
return expr
else:
new_args = tuple(recurse_replace(a,pred,func) for a in expr.args)
if pred(expr):
return func(expr,new_args)
else:
return type(expr)(*new_args)
def rewrite(expr,new_args):
new_args = list(new_args)
pow_val = new_args[1]
pow_val_int = int(new_args[1])
if pow_val.epsilon_eq(pow_val_int):
new_args[1] = s.Integer(pow_val_int)
new_node = type(expr)(*new_args)
return new_node
def isfloatpow(expr):
out = expr.is_Pow and expr.args[1].is_Float
return out
def clean_exponents(expr):
return recurse_replace(expr,isfloatpow,rewrite)
x=s.symbols('x')
expr = (1+x) ** 1.0
s.pprint(expr)
expr2 = recurse_replace(expr,isfloatpow,rewrite)
s.pprint(expr2)
With output
1.0
(x + 1)
x + 1

How to modify arithmetic operators (+,-,x)

I am currently writing a Linear Algebra module for Python 3.x wherein I deal with self-defined matrix objects.
Is there any way I can make the basic arithmetic operators like +, -, * adhere to my matrix objects? For example -
>>> A = matrix("1 2;3 4")
>>> B = matrix("1 0; 0 1")
>>> A + B
[2 2]
[3 5]
>>> A * A
[7 10]
[15 22]
Right now I have written separate functions for addition, multiplication, etc. but typing A.multiply(A) is much more cumbersome than simply A*A.
You are looking for special methods. Particularly at emulating numerical types section.
Also, as you're trying to implement matrices and matrices are containers, you may find useful to define custom container methods for your type.
UPDATE: Here is an example of custom objects using special methods to implement arithmetical operators:
class Value(object):
def __init__(self, x):
self.x = x
def __add__(self, other):
if not isinstance(other, Value):
raise TypeError
return Value(self.x + other.x)
def __mul__(self, other):
if not isinstance(other, Value):
raise TypeError
return Value(self.x * other.x)
assert (Value(2) + Value(3)).x == 5
assert (Value(2) * Value(3)).x == 6
Unless you're doing this specifically to learn or for practice you should look at numerical python, numpy, which is the de facto standard solution for basic linear algebra and matrices. It has a matrix class which does what you are looking for.
You can override the built in methods for numerical types.
class matrix:
def __init__(self, string_matrix):
self.matrix = string_matrix.split(";")
for i in range(len(self.matrix)):
self.matrix[i] = map(int, self.matrix[i].split())
def __add__(self, other):
return_matrix = [[i for i in row] for row in self.matrix]
for i in range(len(self.matrix)):
for j in range(len(self.matrix[i])):
return_matrix[i][j] = self.matrix[i][j] + other.matrix[i][j]
return list_to_matrix(return_matrix)
def __str__(self):
return repr(self.matrix)
def list_to_matrix(list_matrix):
string_form = ""
for row in list_matrix:
for item in row:
if (item != row[-1]): string_form += str(item) + " "
else: string_form += str(item)
if (row != list_matrix[-1]): string_form += ";"
return matrix(string_form)
You will probably want to include a couple of checks (such as adding matrices of different dimensions, or adding a matrix to something that is not a matrix, etc) but this works as a simple example. Also notice that I'm returning a matrix object using the list_to_matrix() function - if that isn't the desired functionality you can change it pretty readily. You would use a similar process for all other arithmetic functions you need to implement.
Output:
>>> a = matrix("3 4;1 4")
>>> b = matrix("2 3;0 0")
>>> print(a)
[[3, 4], [1, 4]]
>>> print(b)
[[2, 3], [0, 0]]
>>> print(a + b)
[[5, 7], [1, 4]]
As mentioned in one of the other answers, numpy might be a good resource to use for your matrix operations - a lot of this functionality is already built in.
>>> import numpy as np
>>> a = np.matrix("3 4;1 4")
>>> b = np.matrix("2 3;0 0")
>>> print(a)
[[3 4]
[1 4]]
>>> print(b)
[[2 3]
[0 0]]
>>> print(a + b)
[[5 7]
[1 4]]
If you define some special methods within the class, Python will call them for arithmetic operations. An example class that define addition, multiplication, division, and subtraction:
class motar:
def __add__(self, other): return "9999"
def __mul__(self, other): return "8888"
def __sub__(self, other): return "7777"
def __div__(self, other): return "6666"
m = [ motar() for x in range(2) ] # create two instances
# arithmetic operations on class instances will call
# ... the special methods defined above:
print m[0] + m[1]
print m[0] * m[1]
print m[0] - m[1]
print m[0] / m[1]
Gives:
9999
8888
7777
6666

Efficient Vector / Point class in Python

What is the best way of implementing an efficient Vector / Point class (or even better: is there one already), that can be used both in Python 2.7+ and 3.x?
I've found the blender-mathutils, but they seem to only support Python 3.x. Then there's this Vector class, that uses numpy, but it's only a 3D vector. Using a list for a Vector like kivy's vector class (sourcecode) that has static attributes (x and y) seems weird too. (There are all these list-methods.)
At the moment I'm using a class that extends namedtuple (as you can see below), but this has the disadvantage of not being able to change the coordinates. I think this can become a performance problem, when thousands of objects are moving and a new (vector) tuple is created everytime. (right?)
class Vector2D(namedtuple('Vector2D', ('x', 'y'))):
__slots__ = ()
def __abs__(self):
return type(self)(abs(self.x), abs(self.y))
def __int__(self):
return type(self)(int(self.x), int(self.y))
def __add__(self, other):
return type(self)(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return type(self)(self.x - other.x, self.y - other.y)
def __mul__(self, other):
return type(self)(self.x * other, self.y * other)
def __div__(self, other):
return type(self)(self.x / other, self.y / other)
def dot_product(self, other):
return self.x * other.x + self.y * other.y
def distance_to(self, other):
""" uses the Euclidean norm to calculate the distance """
return hypot((self.x - other.x), (self.y - other.y))
Edit: I did some testing and it seems that using numpy.array or numpy.ndarray as a vector is too slow. (For example getting an item takes almost twice as long, not to mention creating an array. I think it's more optimized for doing calculations on a large number of items.)
So, I'm looking more for a lightweight vector class with a fixed number of fields (in my case just x and y) that can be used for games. (I don't want to re-invent the wheel if there's already a well-tested one.)
Yeah, there is a vector class: it's in the de facto standard NumPy module. You create vectors like so:
>>> v = numpy.array([1, 10, 123])
>>> 2*v
array([ 2, 20, 246])
>>> u = numpy.array([1, 1, 1])
>>> v-u
array([ 0, 9, 122])
NumPy is very rich and gives you access to fast array operations: dot product (numpy.dot()), norm (numpy.linalg.norm()), etc.
The vector class in numpy in terms of linear algebra would probably be the numpy.matrix which is a subclass of numpy.ndarray. It's not cleaner per se but it makes your code cleaner because algebraic operations are assumed instead of elementwise.
In [77]: a = np.array([1,2])
In [78]: b = np.array([3,3])
In [79]: a*b
Out[79]: array([3, 6])
In [80]: np.dot(a,b)
Out[80]: 9
In [81]: np.outer(a,b)
Out[81]:
array([[3, 3],
[6, 6]])
In [82]: a = np.matrix(a).T
In [83]: b = np.matrix(b)
In [84]: b*a
Out[84]: matrix([[9]])
In [85]: a*b
Out[85]:
matrix([[3, 3],
[6, 6]])
If you want to create your own, base it on one of these, for example:
class v2d(np.ndarray):
def __abs__(self):
return np.linalg.norm(self)
def dist(self,other):
return np.linalg.norm(self-other)
def dot(self, other):
return np.dot(self, other)
# and so on
Which in the simplest case you can just make by viewing an ndarray as your new class:
In [63]: a = np.array([1,2]).view(v2d)
In [64]: b = np.array([3,3]).view(v2d)
In [65]: a
Out[65]: v2d([1, 2])
In [66]: abs(b)
Out[66]: 4.2426406871192848
In [67]: a - b
Out[67]: v2d([-2, -1])
In [68]: a*b
Out[68]: v2d([3, 6])
In [69]: a*3
Out[69]: v2d([3, 6])
In [70]: a.dist(b)
Out[70]: 2.2360679774997898
In [71]: b.dist(a)
Out[71]: 2.2360679774997898
In [72]: a.dot(b)
Out[72]: 9
Here is more information on subclassing the ndarray.
I needed a quick solution as well so I just wrapped numpy's array into my own. You'll notice some design decisions which can be changed to fit your own needs (like defaults).
If you'd like to use it: https://gist.github.com/eigencoder/c029d7557e1f0828aec5
import numpy as np
class Point(np.ndarray):
"""
n-dimensional point used for locations.
inherits +, -, * (as dot-product)
> p1 = Point([1, 2])
> p2 = Point([4, 5])
> p1 + p2
Point([5, 7])
See ``test()`` for more usage.
"""
def __new__(cls, input_array=(0, 0)):
"""
:param cls:
:param input_array: Defaults to 2d origin
"""
obj = np.asarray(input_array).view(cls)
return obj
#property
def x(self):
return self[0]
#property
def y(self):
return self[1]
#property
def z(self):
"""
:return: 3rd dimension element. 0 if not defined
"""
try:
return self[2]
except IndexError:
return 0
def __eq__(self, other):
return np.array_equal(self, other)
def __ne__(self, other):
return not np.array_equal(self, other)
def __iter__(self):
for x in np.nditer(self):
yield x.item()
def dist(self, other):
"""
Both points must have the same dimensions
:return: Euclidean distance
"""
return np.linalg.norm(self - other)
def test():
v1 = Point([1, 2, 3])
v2 = Point([4, 5, 7])
v3 = Point([4, ])
sum12 = Point([5, 7, 10])
dot12 = Point([4, 10, 21])
# Access
assert v2.x == 4
assert v2.y == 5
assert v2.z == 7
assert v3.z == 0
assert Point().x == 0
assert v2[0] == 4
assert v1[-1] == 3 # Not needed but inherited
assert [x for x in v2] == [4, 5, 7], "Iteration should return all elements"
# Operations
assert v1 + v2 == sum12
assert v1 * v2 == dot12
assert v1.dist(v2) ** 2 == 34
assert v1 != v2
assert v2.size == 3, "v2 should be a 3d point"
print "pass"
if __name__ == "__main__":
test()

python: getting around division by zero

I have a big data set of floating point numbers. I iterate through them and evaluate np.log(x) for each of them.
I get
RuntimeWarning: divide by zero encountered in log
I would like to get around this and return 0 if this error occurs.
I am thinking of defining a new function:
def safe_ln(x):
#returns: ln(x) but replaces -inf with 0
l = np.log(x)
#if l = -inf:
l = 0
return l
Basically,I need a way of testing that the output is -inf but I don't know how to proceed.
Thank you for your help!
You are using a np function, so I can safely guess that you are working on a numpy array?
Then the most efficient way to do this is to use the where function instead of a for loop
myarray= np.random.randint(10,size=10)
result = np.where(myarray>0, np.log(myarray), 0)
otherwise you can simply use the log function and then patch the hole:
myarray= np.random.randint(10,size=10)
result = np.log(myarray)
result[result==-np.inf]=0
The np.log function return correctly -inf when used on a value of 0, so are you sure that you want to return a 0? if somewhere you have to revert to the original value, you are going to experience some problem, changing zeros into ones...
Since the log for x=0 is minus infinite, I'd simply check if the input value is zero and return whatever you want there:
def safe_ln(x):
if x <= 0:
return 0
return math.log(x)
EDIT: small edit: you should check for all values smaller than or equal to 0.
EDIT 2: np.log is of course a function to calculate on a numpy array, for single values you should use math.log. This is how the above function looks with numpy:
def safe_ln(x, minval=0.0000000001):
return np.log(x.clip(min=minval))
You can do this.
def safe_ln(x):
try:
l = np.log(x)
except ZeroDivisionError:
l = 0
return l
I like to use sys.float_info.min as follows:
>>> import numpy as np
>>> import sys
>>> arr = np.linspace(0.0, 1.0, 3)
>>> print(arr)
[0. 0.5 1. ]
>>> arr[arr < sys.float_info.min] = sys.float_info.min
>>> print(arr)
[2.22507386e-308 5.00000000e-001 1.00000000e+000]
>>> np.log10(arr)
array([-3.07652656e+02, -3.01029996e-01, 0.00000000e+00])
Other answers have also introduced small positive values, but I prefer to use the smallest possible value to make the approximation more accurate.
The answer given by Enrico is nice, but both solutions result in a warning:
RuntimeWarning: divide by zero encountered in log
As an alternative, we can still use the where function but only execute the main computation where it is appropriate:
# alternative implementation -- a bit more typing but avoids warnings.
loc = np.where(myarray>0)
result2 = np.zeros_like(myarray, dtype=float)
result2[loc] =np.log(myarray[loc])
# answer from Enrico...
myarray= np.random.randint(10,size=10)
result = np.where(myarray>0, np.log(myarray), 0)
# check it is giving right solution:
print(np.allclose(result, result2))
My use case was for division, but the principle is clearly the same:
x = np.random.randint(10, size=10)
divisor = np.ones(10,)
divisor[3] = 0 # make one divisor invalid
y = np.zeros_like(divisor, dtype=float)
loc = np.where(divisor>0) # (or !=0 if your data could have -ve values)
y[loc] = x[loc] / divisor[loc]
use exception handling:
In [27]: def safe_ln(x):
try:
return math.log(x)
except ValueError: # np.log(x) might raise some other error though
return float("-inf")
....:
In [28]: safe_ln(0)
Out[28]: -inf
In [29]: safe_ln(1)
Out[29]: 0.0
In [30]: safe_ln(-100)
Out[30]: -inf
you could do:
def safe_ln(x):
#returns: ln(x) but replaces -inf with 0
try:
l = np.log(x)
except RunTimeWarning:
l = 0
return l
For those looking for a np.log solution that intakes a np.ndarray and nudges up only zero values:
import sys
import numpy as np
def smarter_nextafter(x: np.ndarray) -> np.ndarray:
safe_x = np.where(x != 0, x, np.nextafter(x, 1))
return np.log(safe_x)
def clip_usage(x: np.ndarray, safe_min: float | None = None) -> np.ndarray:
# Inspiration: https://stackoverflow.com/a/13497931/
clipped_x = x.clip(min=safe_min or np.finfo(x.dtype).min)
return np.log(clipped_x)
def inplace_usage(x: np.ndarray, safe_min: float | None = None) -> np.ndarray:
# Inspiration: https://stackoverflow.com/a/62292638/
x[x == 0] = safe_min or np.finfo(x.dtype).min
return np.log(x)
Or if you don't mind nudging all values and like bad big-O runtimes:
def brute_nextafter(x: np.ndarray) -> np.ndarray:
# Just for reference, don't use this
while not x.all():
x = np.nextafter(x, 1)
return np.log(x)

Categories

Resources