Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port from mathutils.Vector to numpy ndarray #10

Open
polarkernel opened this issue Jun 17, 2021 · 5 comments
Open

Port from mathutils.Vector to numpy ndarray #10

polarkernel opened this issue Jun 17, 2021 · 5 comments

Comments

@polarkernel
Copy link
Collaborator

For consistency, the library shall use numpy arrays instead of mathutils.Vector. The development is moved to the branch dev_numpy.

@polarkernel
Copy link
Collaborator Author

I have committed a first running version of the library, which uses a simple, not yet very optimized, port to numpy. Surprizingly, it is much slower than the previous version using mathutils.Vector. I am not yet sure if this port is a good idea. In the following I highlight some issues that explain the reasons just by numbers.

numpy is slow to compute the magnitude of a vector

The magnitude is quite often computed in the library. I define a 2D vector in both systems:

A_mu = mathutils.Vector((1.543,5.497))
A_np = np.array((1.543,5.497))

Then I measured the execution time, even using a square root is faster than numpy.linalg.norm:

print( 'mathutils:',timeit.timeit("a = A_mu.magnitude", number=1000000, globals=globals()) )
print( 'numpy 1:  ',timeit.timeit("a = np.linalg.norm(A_np)", number=1000000, globals=globals()) )
print( 'numpy 2:  ',timeit.timeit("a = np.sqrt(A_np[0]**2+A_np[1]**2)", number=1000000, globals=globals()) )

> mathutils: 0.1049282
> numpy 1:   13.433569
> numpy 2:   2.1385508

numpy is slow to normalize to a unit vector:

print( 'mathutils:',timeit.timeit("a = A_mu.normalized()", number=1000000, globals=globals()) )
print( 'numpy 1:  ',timeit.timeit("a = A_np / np.linalg.norm(A_np)", number=1000000, globals=globals()) )
print( 'numpy 2:  ',timeit.timeit("a = A_np / np.sqrt(A_np[0]**2+A_np[1]**2)", number=1000000, globals=globals()) )

> mathutils:   0.1791930
> numpy 1:     15.281904
> numpy 2:     3.3529117

numpy has no equality operator for vectors

print( 'mathutils:',timeit.timeit("a = A_mu == B_mu", number=1000000, globals=globals()) )
print( 'numpy:    ',timeit.timeit("a = (A_np == B_np).all(0)", number=1000000, globals=globals()) )

> mathutils: 0.1171712
> numpy:     4.8632144

therefore, checking the presence in a list is slow

T_mu is a mthutils.Vector and L_mu is a list of such vectors. T_np is a numpy vectorr and L_np is a list of such vectors. B_np is an array (a block) with numpy vectors a rows.

print( 'mathutils  : ',timeit.timeit("a = T_mu in L_mu", number=1000000, globals=globals()) )
print( 'numpy list : ',timeit.timeit("a = any((np.array(L_np)[:]==T_np).all(1))", number=1000000, globals=globals()) )
print( 'numpy block: ',timeit.timeit("a = any((B_np[:]==T_np).all(1))", number=1000000, globals=globals()) )

> mathutils  :  0.18083845
> numpy list :  16.5957837
> numpy block:  5.24514710

Other issue

In contrast to a list, a numpy array is constructed as a contiguous memory block and can't be used to append or extend elements, as it is possible for a list. The function vstack() allows to append rows, but the result is in a new memory block and gets copied from the old.

verts = np.vstack((verts,skeleton_nodes3D))

Therefore, the argument verts in the argument list of polygonize() does not get appended by the source vertices of the skeleton. For this experiment I just returned it as a function result (see also in demo.py).

Attached here my test code discussed above: test_numpy_speed.py.zip

@vvoovv
Copy link
Member

vvoovv commented Jun 18, 2021

I am not yet sure if this port is a good idea.

Yes, I doesn't seem to be a good idea.

I reproduced your results in Blender's Python.

I got the same results for the dot and cross products:

print( 'mathutils:', timeit.timeit("a = A_mu.dot(B_mu)", number=1000000, globals=globals()) )
print( 'numpy:', timeit.timeit("a = np.dot(A_np, B_np)", number=1000000, globals=globals()) )

print( 'mathutils:', timeit.timeit("a = A_mu.cross(B_mu)", number=1000000, globals=globals()) )
print( 'numpy:', timeit.timeit("a = np.cross(A_np, B_np)", number=1000000, globals=globals()) )

@vvoovv
Copy link
Member

vvoovv commented Jun 18, 2021

I got the same execution time for mathutils and numpy for the dot product of two vectors with 10.000 elements.

@vvoovv
Copy link
Member

vvoovv commented Jun 18, 2021

The conversion to numpy wasn't certainly a good idea.

@costika1234
Copy link
Contributor

Plain Python is better than either numpy or mathutils. See: #16 (comment).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants