how to broadcast a sympy lambdify generated function? - python-3.x

I'd like to compute a Sympy expression as a function of two of the symbols in it. A function of 1 variable can readily be broadcast-ed after lambdifying it:
x, y, z = symbols('x y z')
expr = x*y + z
f = lambdify(x, expr.subs({z:2, y:4}))
x = np.linspace(1, 4, 5)
f(x)
But is there a way to use some builtin capability of numpy or sympy to broadcast higher dimensionally? In other words, is there a more direct or cleaner way to do the following?
x, y, z = symbols('x y z')
expr = x*y + z
f = lambdify([x, y], expr.subs({z:2}))
def g(xy):
k = xy.shape[1]
a = np.ndarray((k,k))
for j in range(k):
for i in range(k):
a[j, i] = f(xy[0, j], xy[1, i])
return a
x = np.linspace(0, 4, 5)
y = np.linspace(10, 12, 5)
xy = np.array([x,y])
g(xy)

You just need to get the shapes right for broadcasting:
In [11]: x, y, z = symbols('x y z')
...: expr = x*y + z
...: f = lambdify([x, y], expr.subs({z:2}))
In [12]: x = np.linspace(0, 4, 5).reshape((5, 1))
In [13]: y = np.linspace(10, 12, 5)
In [14]: f(x, y)
Out[14]:
array([[ 2. , 2. , 2. , 2. , 2. ],
[12. , 12.5, 13. , 13.5, 14. ],
[22. , 23. , 24. , 25. , 26. ],
[32. , 33.5, 35. , 36.5, 38. ],
[42. , 44. , 46. , 48. , 50. ]])

help(f) (or print(f.__doc__)) shows:
Help on function _lambdifygenerated:
_lambdifygenerated(x, y)
Created with lambdify. Signature:
func(x, y)
Expression:
x*y + 2
Source code:
def _lambdifygenerated(x, y):
return (x*y + 2)
The python/numpy function just does x*y+2. That's a simple translation of the sympy. Use standard numpy array broadcasting.
For example a (3,1) array with (2,) produces a (3,2) result:
In [35]: np.arange(3)[:,None] * np.arange(10,12) + 2
Out[35]:
array([[ 2, 2],
[12, 13],
[22, 24]])
In [36]: f(np.arange(3)[:,None], np.arange(10,12))
Out[36]:
array([[ 2, 2],
[12, 13],
[22, 24]])

Related

pytorch's grid_sample return an incorrect value

I have a 3D matrix: img[i, j, k] = i+j+k.
In my opinion, if I want the value of (1, 2, 3), the grid_sample should return 6. But it not.
The code is:
import torch
from torch.nn import functional as F
import numpy as np
X, Y, Z = 10, 20, 30
img = np.zeros(shape=[X, Y, Z], dtype=np.float32)
for i in range(X):
for j in range(Y):
for k in range(Z):
img[i,j,k] = i+j+k
inp = torch.from_numpy(img).unsqueeze(0).unsqueeze(0)
grid = torch.from_numpy(np.array([[1, 2, 3]], dtype=np.float32)).unsqueeze(1).unsqueeze(1).unsqueeze(1)
grid[..., 0] /= (X-1)
grid[..., 1] /= (Y-1)
grid[..., 2] /= (Z-1)
grid = 2*grid - 1
outp = F.grid_sample(inp, grid=grid, mode='bilinear', align_corners=True)
print(outp)
The grid_sample return 6.15. Is there anything wrong with my code?
Finally, I find the solution. The reason why the above code return an incorrect value is that the torch.grid_sample accept (z, y, x) point.
Thus, the correct code should be:
import torch
from torch.nn import functional as F
import numpy as np
X, Y, Z = 10, 20, 30
img = np.zeros(shape=[X, Y, Z], dtype=np.float32)
for i in range(X):
for j in range(Y):
for k in range(Z):
img[i,j,k] = i+j+k
inp = torch.from_numpy(img).unsqueeze(0).unsqueeze(0)
grid = torch.from_numpy(np.array([[1, 2, 3]], dtype=np.float32)).unsqueeze(1).unsqueeze(1).unsqueeze(1)
grid[..., 0] /= (X-1)
grid[..., 1] /= (Y-1)
grid[..., 2] /= (Z-1)
grid = 2*grid - 1
newgrid = grid.clone()
newgrid[..., 0] = grid[..., 2]
newgrid[..., 1] = grid[..., 1]
newgrid[..., 2] = grid[..., 0]
outp = F.grid_sample(inp, grid=newgrid, mode='bilinear', align_corners=True)
print(outp)

Remove non-conjugates from complex numbers list

I have two lists, one contains the real part of imaginary numbers, the other contains the imaginary part of the same numbers. I want to remove from both lists the imaginary numbers that do not have a conjugate.
For example, the following lists x = [3, 4, 2, 7, 4] and y = [2, -1, 0, 6, 1] represent the numbers :
3 + 2j <- no conjugate (to remove)
4 - 1j <- conjugate (to keep)
2 + 0j <- real (to keep)
4 + 1j <- conjugate (to keep)
7 + 6j <- no conjugate (to remove)
The expected result is the following :
new_x = [4, 2, 4]
new_y = [-1, 0, 1]
Any idea how i can achieve this ? Thanks
This script will find complex conjugates from lists x and y:
x = [3, 4, 2, 7, 4]
y = [2, -1, 0, 6, 1]
tmp = {}
for r, i in zip(x, y):
tmp.setdefault(i, set()).add(r)
x_out, y_out = [], []
for r, i in zip(x, y):
if i==0 or r in tmp.get(-i, []):
x_out.append(r)
y_out.append(i)
print(x_out)
print(y_out)
Prints:
[4, 2, 4]
[-1, 0, 1]

Optimization of loops - How to implement list-comprehensions

I am writing a function, get_moduli,
that takes an arbitrary number of lists of Cartesian coordinates as input,
and returns the radii as a list. The function behaves correctly but is a bit too long,
and could perhaps be optimized using list comprehensions. Any suggestions?
# Sample input:
x = [1, 2, 3]
y = [1, 3, 5]
z = [1, 4, 7]
def get_moduli(*args):
"""Moduli of arbitrary number of vectors."""
moduli = []
for container in args:
for i in range(len(container)):
container[i - 1] = container[i - 1] ** 2
for i in range(len(args)):
sum_of_squares = 0
for j in range(len(args[i])):
sum_of_squares += args[j][i]
moduli.append(sum_of_squares ** .5)
return moduli
# Run function on sample input:
get_moduli(x, y, z)
# Desired outcome should be as follows:
# [(1 ** 2 + 1 ** 2 + 1 ** 2) ** .5,
# (2 ** 2 + 3 ** 2 + 4 ** 2) ** .5,
# (3 ** 2 + 5 ** 2 + 7 ** 2) ** .5]
Returns correct answer:
[1.7320508075688772, 5.385164807134504, 9.1104335791443]
But how could this function be simplified?
These two functions do what you want:
def modulus(v):
return sum(vi*vi for vi in v)**0.5
def zipped_modulus(*args):
return [modulus(t) for t in list(zip(*args))]
zipped_modulus(x,y,z)
Took me some time to figure out that your matrix is transposed of what i thought
you can use this
def modulus(*args)
return [sum([v**2 for v in vector])**.5 for vector in zip(*args)]
# Sample input:
x = [1, 2, 3]
y = [1, 3, 5]
z = [1, 4, 7]
def get_moduli1(*args):
return list(map(lambda x: sum(y**2 for y in x)**0.5 , zip(*args)))
print(get_moduli1(x, y, z))
output
[1.7320508075688772, 5.385164807134504, 9.1104335791443]

Reduce multiplte arrays based on coordinate values

I have 5 lists:
X = [0,1,2,3,4,0,1,2,3,6]
Y = [9,8,7,6,4,9,4,7,6,3]
R = [1,2,3,4,5,6,7,8,9,0]
P = [2,4,6,8,10,12,14,16,18,20]
Q = [1,3,5,7,9,11,13,15,17,19]
Given duplicate coordinates I want to sum the attributes that refer to the coordinates so for example X[0] = 0 and Y[0] = 9 this point is repeated at X[5] and Y[5] but with different R, P, Q values R[0] != R[5] and so on.
I am trying to produce a list with unique coordinate and summed values of the duplicate coordinates to produce new X, Y, R, P, Q that look like this:
X = [0,1,2,3,4,1,6]
Y = [9,8,7,6,4,4,3]
R = [7,2,11,13,5,7,0]
P = [14,4,22,26,10,14,20]
Q = [14,3,20,24,9,11,19]
I am not able to formulate this problem, any help is appreciated!
If you use pandas it would look like this:
import pandas as pd
X = [0,1,2,3,4,0,1,2,3,6]
Y = [9,8,7,6,4,9,4,7,6,3]
R = [1,2,3,4,5,6,7,8,9,0]
P = [2,4,6,8,10,12,14,16,18,20]
Q = [1,3,5,7,9,11,13,15,17,19]
df = pd.DataFrame([X, Y, R, P, Q])
X, Y, R, P, Q = df.T.groupby([0,1]).sum().reset_index().T.values
Which would produce:
[0 1 1 2 3 4 6]
[9 4 8 7 6 4 3]
[ 7 7 2 11 13 5 0]
[14 14 4 22 26 10 20]
[12 13 3 20 24 9 19]
Take note that order is not preserved but numbers match.
Here's another solution using Numpy:
import numpy as np
X = np.array([0,1,2,3,4,0,1,2,3,6])
Y = np.array([9,8,7,6,4,9,4,7,6,3])
R = np.array([1,2,3,4,5,6,7,8,9,0])
P = np.array([2,4,6,8,10,12,14,16,18,20])
Q = np.array([1,3,5,7,9,11,13,15,17,19])
coords = np.array(list(zip(X,Y)), dtype=[('f0', '<i4'), ('f1', '<i4')])
unique_coords = np.unique(coords)
X_new = [x[0] for x in unique_coords]
Y_new = [y[1] for y in unique_coords]
R_new = [np.sum(R[coords == coo]) for coo in unique_coords]
P_new = [np.sum(P[coords == coo]) for coo in unique_coords]
Q_new = [np.sum(Q[coords == coo]) for coo in unique_coords]
print(X_new)
print(Y_new)
print(R_new)
print(P_new)
print(Q_new)
Output:
[0, 1, 1, 2, 3, 4, 6]
[9, 4, 8, 7, 6, 4, 3]
[7, 7, 2, 11, 13, 5, 0]
[14, 14, 4, 22, 26, 10, 20]
[12, 13, 3, 20, 24, 9, 19]

How do I find x given y or vice versa in numpy?

For example, I have the following arrays:
x = [0, 1, 2, 3, 4.5, 5]
y = [2, 8, 3, 7, 8, 1]
I would like to be able to do the following given x:
>>> what_is_y_when_x_is(2)
(2, 3)
>>> what_is_y_when_x_is(3.1) # Perhaps set rules to round to nearest (or up or down)
(3, 7)
On the other hand, when given y:
>>> what_is_x_when_y_is(2)
(0, 2)
>>> what_is_x_when_y_is(max(y))
([1, 4.5], 8)
The circumstances of this problem
I could have plotted y versus x using a closed analytical function, which should be very easy by just calling foo_function(x). However, I'm running numerical simulations whose data plots do not have closed analytical solutions.
Attempted solution
I've tackled similar problems before and approached them roughly this way:
what_is_y_when_x_is(some_x)
Search the array x for some_x.
Get its index, i.
Pick up y[i].
Question
Is there a better way to do this? Perhaps a built-in numpy function or a better algorithm?
You should look at numpy.searchsorted and also numpy.interp. Both of those look like they might do the trick. Here is an example:
import numpy as np
x = np.array([0, 1, 2, 3, 4.5, 5])
y = np.array([2, 8, 3, 7, 8, 1])
# y should be sorted for both of these methods
order = y.argsort()
y = y[order]
x = x[order]
def what_is_x_when_y_is(input, x, y):
return x[y.searchsorted(input, 'left')]
def interp_x_from_y(input, x, y):
return np.interp(input, y, x)
print what_is_x_when_y_is(7, x, y)
# 3
print interp_x_from_y(1.5, x, y)
# 2.5
You could use the bisect module for this. This is pure python - no numpy here:
>>> x = [0, 1, 2, 3, 4.5, 5]
>>> y = [2, 8, 3, 7, 8, 1]
>>> x_lookup = sorted(zip(x, y))
>>> y_lookup = sorted(map(tuple, map(reversed, zip(x, y))))
>>>
>>> import bisect
>>> def pair_from_x(x):
... return x_lookup[min(bisect.bisect_left(x_lookup, (x,)), len(x_lookup)-1)]
...
>>> def pair_from_y(y):
... return tuple(reversed(y_lookup[min(bisect.bisect_left(y_lookup, (y,)), len(y_lookup)-1)]))
...
And some examples of using it:
>>> pair_from_x(0)
(0, 2)
>>> pair_from_x(-2)
(0, 2)
>>> pair_from_x(2)
(2, 3)
>>> pair_from_x(3)
(3, 7)
>>> pair_from_x(7)
(5, 1)
>>>
>>> pair_from_y(0)
(5, 1)
>>> pair_from_y(1)
(5, 1)
>>> pair_from_y(3)
(2, 3)
>>> pair_from_y(4)
(3, 7)
>>> pair_from_y(8)
(1, 8)
The way you described is, as far as I'm considered, a good way. I'm not sure if you are, but I think you could use the .index(...) method on your array:
>>> li
['I', 'hope', 'this', 'answer', 'helps', 'you']
>>> li.index("hope")
1
Other than that, you might want to consider one array op "Points" which have an x and a y, though I'm not sure if this is possible of course. That way you won't have to keep two arrays in sync (same number of elements).
I don't see any problem with your pipeline. You can write a snippet base on numpy.where to implement it efficiently. Note that you will have to pass your lists as numpy arrays first (this can be included in the function).
Below is an example of a function doing the job, with an option to round the target (I have included a which array argument, so everything can be done in just one function, whatever you want to search in x or y). Note that one of the output will be a numpy array, so modify it to convert it to anything you want (liste, tuple, etc ...).
import numpy as np
def pick(x_array, y_array, target, which_array='x', round=True):
# ensure that x and y are numpy arrays
x_array, y_array = np.array(x_array), np.array(y_array)
# optional: round to the nearest. True by default
if round==True:
target = np.round(target)
if which_array == 'x': # look for the target in x_array
return target, y_array[np.where(x_array == target)[0]]
if which_array == 'y': # look for the target in y_array
return x_array[np.where(y_array == target)[0]], target
Results given by your examples:
# >>> what_is_y_when_x_is(2)
pick(x, y, 2, 'x')
(2, array([3]))
# >>> what_is_y_when_x_is(3.1)
pick(x, y, 3.1, 'x')
3.0, array([7]))
# >>> what_is_y_when_x_is(2)
pick(x, y, 2, 'y')
(array([ 0.]), 2)
# >>> what_is_x_when_y_is(max(y))
pick(x, y, max(y), 'y')
(array([ 1. , 4.5]), 8)
Here is a revised version of code provided by # Bi Rico:
import numpy as np
x = np.array([0, 1, 2, 3, 4.5, 5])
y = np.array([2, 8, 3, 7, 8, 1])
# y should be sorted for both of these methods
order = np.argsort(y)
y = y[order]
x = x[order]
def what_is_x_when_y_is(input, x, y):
return x[y.searchsorted(input, 'left')]
print(what_is_x_when_y_is(7, x, y))
# 3
This worked for me:
def what_is_y_when_x_is(value, x, y, tolerance=1e-3):
return [(xi, yi) for (xi, yi) in zip(x, y) if abs(xi - value) <= tolerance]
Notice that rather than comparing for equality the code above performs a "close enough" equality test. The default tolerance is set up to 0.001 (you can use any other value). Here are some examples of use:
>>> x = [0, 1, 2, 3, 4.5, 5]
>>> y = [2, 8, 3, 7, 8, 1]
>>> what_is_y_when_x_is(0, x, y)
[(0, 2)]
>>> what_is_y_when_x_is(1, x, y, tolerance=.1)
[(1, 8)]
>>> what_is_y_when_x_is(2, x, y, tolerance=1)
[(1, 8), (2, 3), (3, 7)]
>>> what_is_y_when_x_is(4, x, y, tolerance=.5)
[(4.5, 8)]

Resources