How to extract the BSpline basis from scipy.interpolate.BSpline - python

In this question I asked the community about how scipy.interpolate.splev calculates a spline basis.. My goal was to compute a spline faster then splev by pre-calculating a bspline basis and generate a curve by doing a basis to control point dot product.
Since then a new scipy.interpolate.BSpline interpolator was added to scipy. It comes with a basis_element function, which I presume could be used to return the basis used to calculate a spline.
So for example using the code from here with the inputs below:
import numpy as np
# Control points
cv = np.array([[ 50., 25., 0.],
[ 59., 12., 0.],
[ 50., 10., 0.],
[ 57., 2., 0.],
[ 40., 4., 0.],
[ 40., 14., 0.]])
kv = [0, 0, 0, 0, 1, 2, 3, 3, 3, 3] # knot vector
n = 10 # 10 samples (keeping it simple)
degree = 3 # Curve degree
I can compute the following bspline basis:
[[ 1. 0. 0. 0. 0. 0. ]
[ 0.2962963 0.56481481 0.13271605 0.00617284 0. 0. ]
[ 0.03703704 0.51851852 0.39506173 0.04938272 0. 0. ]
[ 0. 0.25 0.58333333 0.16666667 0. 0. ]
[ 0. 0.07407407 0.54938272 0.36728395 0.00925926 0. ]
[ 0. 0.00925926 0.36728395 0.54938272 0.07407407 0. ]
[ 0. 0. 0.16666667 0.58333333 0.25 0. ]
[ 0. 0. 0.04938272 0.39506173 0.51851852 0.03703704]
[ 0. 0. 0.00617284 0.13271605 0.56481481 0.2962963 ]
[ 0. 0. 0. 0. 0. 1. ]]
Using np.dot with basis and control points returns 10 samples on curve:
[[ 50. 25. 0. ]
[ 55.12654321 15.52469136 0. ]
[ 55.01234568 11.19753086 0. ]
[ 53.41666667 9.16666667 0. ]
[ 53.14506173 7.15432099 0. ]
[ 53.1882716 5.17901235 0. ]
[ 51.58333333 3.83333333 0. ]
[ 47.20987654 3.87654321 0. ]
[ 42.31790123 6.7345679 0. ]
[ 40. 14. 0. ]]
Question : is it possible to extract the basis as described above out of scipy.interpolate.BSpline?
Obviously I must be using it wrong, because when I try I get something like this:
from scipy.interpolate import BSpline
b = BSpline.basis_element(kv)
print b(np.linspace(kv[0],kv[-1],n)) # i'm not sure what these values represent
[ 0. 0.00256299 0.04495618 0.16555213 0.28691315 0.28691315
0.16555213 0.04495618 0.00256299 0. ]

BSpline.basis_element takes as its arguments the internal knots.
In your example, you padded the knots, and that did not do what you thought it would:
In [3]: t = [0, 0, 0, 0, 1, 2, 3, 3, 3, 3]
In [4]: b = BSpline.basis_element(t)
In [5]: b.k
Out[5]: 8
So it's an 8th order spline.
If you wanted a quadratic spline, you would do
In [7]: b1 = BSpline.basis_element([0, 1, 2, 3])
In [8]: b1.k
Out[8]: 2
In [9]: b1.t
Out[9]: array([-1., -1., 0., 1., 2., 3., 4., 4.])
Confused? The method is quite simple: https://github.com/scipy/scipy/blob/v0.19.1/scipy/interpolate/_bsplines.py#L243-L302
The callable returned by BSpline.basis_element is really a b-spline function. The result of calling it with an array argument is then equivalent to directly running the example code in the BSpline docstring in a loop for each element of your array, https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.BSpline.html
EDIT: if you're after a variant of Cox-de Boor algorithm of calculating all non-zero splines at a given point, then you can look at a _bspl.evaluate_all_bsplines function, https://github.com/scipy/scipy/blob/v0.19.1/scipy/interpolate/_bspl.pyx#L161
(which itself is just a wrapper over a C routine which does all the heavy lifting; note that it's hard to beat that latter one performance wise.)
However, it's not a public function, so it's not guaranteed to be available in future versions. If you have a good use for it, and a suggestion for a user-facing API, bring the discussion over to the scipy bug tracker.

Related

Inserting complex functions in a python code

I have been trying to insert $e^ix$ as matrix element.
The main aim is to find the eigenvalue of a matrix which has many complex functions as elements. Can anyone help me how to insert it? My failed attempt is below:
for i in range(0,size):
H[i,i]=-2*(cmath.exp((i+1)*aj))
H[i,i+1]=1.0
H[i,i-1]=1.0
'a' is defined earlier in the program. The error flagged shows that aj is not defined. Using cmath I thought a complex number can be expontiated as (x+yj). Unfortunately, I couldn't figure out the right way to use it. Any help would be appreciated
Define a small float array:
In [214]: H = np.eye(3)
In [215]: H
Out[215]:
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
Create a complex number:
In [216]: 1+3j
Out[216]: (1+3j)
In [217]: np.exp(1+3j)
Out[217]: (-2.6910786138197937+0.383603953541131j)
Trying to assign it to H:
In [218]: H[1,1]=np.exp(1+3j)
<ipython-input-218-6c0b228d2833>:1: ComplexWarning: Casting complex values to real discards the imaginary part
H[1,1]=np.exp(1+3j)
In [219]: H
Out[219]:
array([[ 1. , 0. , 0. ],
[ 0. , -2.69107861, 0. ],
[ 0. , 0. , 1. ]])
Now make an complex dtype array:
In [221]: H = np.eye(3).astype( complex)
In [222]: H[1,1]=np.exp(1+3j)
In [223]: H
Out[223]:
array([[ 1. +0.j , 0. +0.j ,
0. +0.j ],
[ 0. +0.j , -2.69107861+0.38360395j,
0. +0.j ],
[ 0. +0.j , 0. +0.j ,
1. +0.j ]])
edit
For an array of values:
In [225]: a = np.array([1,2,3])
In [226]: np.exp(a+1j*a)
Out[226]:
array([ 1.46869394+2.28735529j, -3.07493232+6.7188497j ,
-19.88453084+2.83447113j])
In [228]: H[:,0]=np.exp(a+1j*a)
In [229]: H
Out[229]:
array([[ 1.46869394+2.28735529j, 0. +0.j ,
0. +0.j ],
[ -3.07493232+6.7188497j , -2.69107861+0.38360395j,
0. +0.j ],
[-19.88453084+2.83447113j, 0. +0.j ,
1. +0.j ]])

how to put Multiple Matrices Together into a Single Matrix?

I need to put multiple matrices together into a single matrix, like so:
I have the values for the matrix, but I can't get it to appear like how it does in the image- instead, my values end up stacked on top of each other in an array. How can I go about getting my matrices to look like the image above?
My code:
import numpy as np
w_estimate = [0.656540, 7.192304, 2.749036]
F = [np.identity(3) * -w_estimate[1:4], -np.identity(3)], [np.identity(3)*0, np.identity(3)*0]
It's supposed to look like:
F = [[np.identity(3) * -w_estimate[1:4], -np.identity(3)]
[np.identity(3) * 0, np.identity(3) * 0]]
but instead it looks like:
[[np.identity(3) * -w_estimate[1:4]],
[-np.identity(3)],
[np.identity(3) * 0],
[np.identity(3) * 0]]
Help is very much appreciated.
The first correction to your code pertains to -w_estimate[1:4].
Since w_estimate is a plain pythonic list, you can not apply
minus operator to it.
You can however apply minus operator to a Numpy array.
Another correction is to avoid -0 in the result.
To get an array with diagonal elements filled from some other array,
and all other zeroes, you can use np.diagonal_fill, which fills
in-place diagonal elements of some (earlier) created array
(using np.zeros).
So to construct 2 "upper" blocks of your result, you can write:
a1 = np.zeros((3,3))
a2 = a1.copy()
np.fill_diagonal(a1, -np.array(w_estimate)[1:4])
np.fill_diagonal(a2, -1)
Note that -np.array(w_estimate)[1:4] returns last 2 elements of
w_estimate them, i.e. [7.192304, 2.749036]. Since the target array
is "3 by 3", the source sequence is repeated (in this case, for the
last diagonal element only).
If your intention is different, change -np.array(w_estimate)[1:4]
accordingly.
And to construct the whole intended array, run:
F = np.vstack((np.hstack((a1, a2)), np.zeros((3,6))))
The result is:
array([[-7.192304, 0. , 0. , -1. , 0. , 0. ],
[ 0. , -2.749036, 0. , 0. , -1. , 0. ],
[ 0. , 0. , -7.192304, 0. , 0. , -1. ],
[ 0. , 0. , 0. , 0. , 0. , 0. ],
[ 0. , 0. , 0. , 0. , 0. , 0. ],
[ 0. , 0. , 0. , 0. , 0. , 0. ]])
You shoud definitely take a look at numpy.block method.
>>> A = np.eye(2) * 2
>>> B = np.eye(3) * 3
>>> np.block([
... [A, np.zeros((2, 3))],
... [np.ones((3, 2)), B ]
... ])
array([[2., 0., 0., 0., 0.],
[0., 2., 0., 0., 0.],
[1., 1., 3., 0., 0.],
[1., 1., 0., 3., 0.],
[1., 1., 0., 0., 3.]])

How do I compute one hot encoding using tf.one_hot?

I'm trying to build a one hot encoding of y_train of mnist data-set using tensorflow. I couldn't understand how to do it?
# unique values 0 - 9
y_train = array([5, 0, 4, ..., 5, 6, 8], dtype=uint8)
In keras we'll do something like
# this converts it into one hot encoding
one hot_encoding = tf.keras.utils.to_categorical(y_train)
Where as in tf.one_hot what should be my input to indices & depth parameters? After doing one hot encoding how can I convert it back to numpy array from 2d-tensor?
I'm not familiar with Tensorflow but after some tests, this is what I've found:
tf.one_hot() takes an indices and a depth. The indices are the values to actually convert to a one-hot encoding. depth refers to the maximum value to utilize.
For example, take the following code:
y = [1, 2, 3, 2, 1]
tf.keras.utils.to_categorical(y)
sess = tf.Session();
with sess.as_default():
print(tf.one_hot(y, 2).eval())
print(tf.one_hot(y, 4).eval())
print(tf.one_hot(y, 6).eval())
tf.keras.utils.to_categorical(y) Returns the following:
array([[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.],
[0., 0., 1., 0.],
[0., 1., 0., 0.]], dtype=float32)
In contrast, the tf.one_hot() options (2, 4, and 6) do the following:
[[0. 1.]
[0. 0.]
[0. 0.]
[0. 0.]
[0. 1.]]
[[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]
[0. 0. 1. 0.]
[0. 1. 0. 0.]]
[[0. 1. 0. 0. 0. 0.]
[0. 0. 1. 0. 0. 0.]
[0. 0. 0. 1. 0. 0.]
[0. 0. 1. 0. 0. 0.]
[0. 1. 0. 0. 0. 0.]]
As can be seen here, to mimic tf.keras.utils.to_categorical() using tf.one_hot(), the depth parameter should be equivalent to the maximum value present in the array, +1 for 0. In this case, the maximum value is 3, so there are four possible values in the encoding - 0, 1, 2, and 3. As such, a depth of 4 is required to represent all of these values in the one-hot encoding.
As for conversion to numpy, as shown above, using a Tensorflow session, running eval() on a tensor converts it to a numpy array. For methods on doing this, refer to How can I convert a tensor into a numpy array in TensorFlow?.
I'm not familiar with Tensorflow but I hope this helps.
Note: for the purposes of MNIST, a depth of 10 should be sufficient.
I'd like to counter what #Andrew Fan has said. First, the above y label list does not start at index 0, which is what is required. Just look at the first column (i.e. index 0) in all of those examples: they're all empty. This will create a redundant class in the learning and may cause problems. One hot creates a simple list with 1 for that index position and zeros elsewhere. Therefore, your depth has to be the same as the number of classes, but you also have to start at index 0.

specific kind of slicing over tensor object in tensorflow

Summary of the question, Is this kind of slicing and then assignment supported in tensorflow?
out[tf_a2[y],x[:,None]] = tf_a1[tf_a2[y],x[:,None]]
final = out[:-1]
Lets give the example, I have a tensor like this:
tf_a1 = tf.Variable([ [9.968594, 8.655439, 0., 0. ],
[0., 8.3356, 0., 8.8974 ],
[0., 0., 6.103182, 7.330564 ],
[6.609862, 0., 3.0614321, 0. ],
[9.497023, 0., 3.8914037, 0. ],
[0., 8.457685, 8.602337, 0. ],
[0., 0., 5.826657, 8.283971 ],
[0., 0., 0., 0. ]])
and I have this one:
tf_a2 = tf.constant([[1, 2, 5],
[1, 4, 6],
[0, 7, 7],
[2, 3, 6],
[2, 4, 7]])
Now I want to keep the elements in tf_a1 in which the combination of n (here n is 2) of them (index of them) is in the value of tf_a2. What does it mean?
For example, in tf_a1, in the first column, indexes which has value are: (0,3,4). Is there any row in tf_a2 which contains any combination of these two indexes: (0,3), (0,4) or (3,4). Actually, there is no such row. So all the elements in that column became zero.
Indexes for the second column in tf_a1 is (0,1) (0,5) (1,5). As you see the record (1,5) is available in the tf_a2 in the first row. That's why we keep those in the tf_a1.
This is the correct numpy code:
y,x = np.where(np.count_nonzero(a1p[a2], axis=1) >= n)
out = np.zeros_like(tf_a1)
out[tf_a2[y],x[:,None]] = tf_a1[tf_a2[y],x[:,None]]
final = out[:-1]
This is the expected output of this numpy code (but I need this in tensorflow):
[[0. 0. 0. 0. ]
[0. 8.3356 0. 8.8974 ]
[0. 0. 6.103182 7.330564 ]
[0. 0. 3.0614321 0. ]
[0. 0. 3.8914037 0. ]
[0. 8.457685 8.602337 0. ]
[0. 0. 5.826657 8.283971 ]]
The tensorflow code should be something like this:
y, x = tf.where(tf.count_nonzero(tf.gather(tf_a1, tf_a2, axis=0), axis=1) >= n)
out = tf.zeros_like(tf_a1)
out[tf_a2[y],x[:,None]] = tf_a1[tf_a2[y],x[:,None]]
final = out[:-1]
This part of the code tf.gather(tf_a1, tf_a2, axis=0), axis=1) is doing the numpy like slicing tf_a1[tf_a2]
Update 1
The only line which does not work its:
out[tf_a2[y],x[:,None]] = tf_a1[tf_a2[y],x[:,None]]
final = out[:-1]
Any idea how can I accomplish this in tensorflow, is this kind of slicing is supported in tensor object at all?
Any help is appreciated:)

Why can't I change numpy's array?

My array is from a image whick like below:
[[[ 66.17041352 32.64576397 20.96214396]
[ 66.17041352 32.64576397 20.96214396]
[ 65.96318838 32.36065031 16.13857633]
...,
[ 69.04849876 28.06324166 26.57747623]
[ 63.7269604 32.96378326 25.94336956]
[ 53.96807994 39.33219382 23.9025511 ]]
...,
[[ 18.55833403 34.4104455 -9.75497344]
[ 18.55833403 34.4104455 -9.75497344]
[ 21.45103128 32.77919479 -3.84284208]
...,
[ 44.64859327 41.89617915 14.25196745]
[ 43.40291913 43.25109885 17.43372679]
[ 43.30009306 47.94315449 15.59464532]]
[[ 18.64249436 31.63054472 -7.56023249]
[ 18.64249436 31.63054472 -7.56023249]
[ 23.23099091 32.284216 -3.86411699]
...,
[ 44.98536772 45.0246078 17.92556564]
[ 45.53417128 45.42120428 17.50264622]
[ 46.7226915 45.42428651 19.21054283]]]
I want to change the array to zero like this:
[[[ 0. 0. 0.]
[ 0. 0. 0.]
[ 0. 0. 0.]
...,
[ 0. 0. 0.]
[ 0. 0. 0.]
[ 0. 0. 0.]]
...,
[[ 0. 0. 0.]
[ 0. 0. 0.]
[ 0. 0. 0.]
...,
[[ 0. 0. 0.]
[ 0. 0. 0.]
[ 0. 0. 0.]
...,
[ 0. 0. 0.]
[ 0. 0. 0.]
[ 0. 0. 0.]]]
I know how to make it happen, but I just wonder why my origin code doesn't work.The print shows that nothing changed.
for row in image_arr:
for col in row:
col = [0,0,0]
print image_arr
In your loop, you are just reassigning the name col to the list [0,0,0], over and over and over again. Not at all what you want! To change your 3d array to all zeros, you simply do this.:
arr[:, :, :,] = 0
Bing bang boom, you're done.
This is because col is a copy not a reference. This is true for all of python:
CODE Example:
simple = [1,2,3,4,5,6]
print simple
for elem in simple:
elem = 0
print simple
OUTPUT:
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6]
Try this instead:
rows,cols,lens = arr.shape
for r in range(rows):
for c in range(cols):
arr[r][c] = [0,0,0]
You change the value col in the loop but it's not related to the original variable image_arr.
You can use enumerate to access the index and modify image_arr variable directly. As in the following example:
import numpy as np
image_arr = np.arange(30).reshape(3, 5, 2)
print(image_arr)
for i,row in enumerate(image_arr):
for j,col in enumerate(row):
image_arr[i][j]=0
print(image_arr)
You are changing col to a new list, but col is not a reference to a sublist of row. Instead, if you change the elements of col, then you will get the result you want:
for row in image_arr:
for col in row:
for i in range(len(col))
col[i] = 0
Is because col is a value not a reference , see Python objects confusion: a=b, modify b and a changes!
try instead :
a=0
b=0
for row in image_arr:
for col in row:
image_arr[a][b] = [0,0,0]
a=a+1
b=b+1
print image_arr
col is a 1d array, a view of arr. To change its values you need to use slicing notation. col=[0,0,0] reassigns the variable without mutating the iteration variable. Mutability is a key concept here (and applicable to lists and dictionaries as well).
In [254]: arr = np.ones((2,3,4))
In [255]: for row in arr:
...: for col in row:
...: col[:] = 0 # or = [1,2,3,5]
...:
In [256]: arr
Out[256]:
array([[[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]],
[[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]]])
And since you want to change all the values, col[:]=0 works just as well as col[:]=[0,0,0,0] (in my shape).
But while we are at it, any of these also work
In [257]: for row in arr:
...: row[:,:] = 1
...:
In [258]: arr
Out[258]:
array([[[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.],
...]]])
In [259]: arr[:,:,:]=2 # one : per dimension
In [260]: arr[...] = 3 # ... shorthand for multiple :
But why reset arr? Why not make a new array with the same shape (and throw away the 'original')?
arr1 = np.zeros_like(arr)

Categories