Skip to content

Commit f1e4b54

Browse files
committed
Add cross, normsq
Make norm faster
1 parent bb6a47d commit f1e4b54

5 files changed

Lines changed: 114 additions & 14 deletions

File tree

‎RELEASE‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.8.5
1+
0.8.7

‎spatialmath/base/__init__.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
'colvec',
112112
'unitvec',
113113
'norm',
114+
'normsq',
114115
'isunitvec',
115116
'iszerovec',
116117
'isunittwist',
@@ -120,6 +121,7 @@
120121
'unittwist2',
121122
'angdiff',
122123
'removesmall',
124+
'cross',
123125
# spatialmath.base.animate
124126
'Animate',
125127
'Animate2',

‎spatialmath/base/test/test_vectors.py‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,32 @@ def test_isunitvec(self):
7676
def test_norm(self):
7777
self.assertAlmostEqual(norm([0, 0, 0]), 0)
7878
self.assertAlmostEqual(norm([1, 2, 3]), math.sqrt(14))
79-
79+
self.assertAlmostEqual(norm(np.r_[1, 2, 3]), math.sqrt(14))
80+
8081
x, y = sym.symbol('x y')
8182
v = [x, y]
8283
self.assertEqual(norm(v), sym.sqrt(x**2 + y**2))
8384
self.assertEqual(norm(np.r_[v]), sym.sqrt(x**2 + y**2))
8485

86+
def test_norm(self):
87+
self.assertAlmostEqual(norm([0, 0, 0]), 0)
88+
self.assertAlmostEqual(normsq([1, 2, 3]), 14)
89+
self.assertAlmostEqual(normsq(np.r_[1, 2, 3]), 14)
90+
91+
x, y = sym.symbol('x y')
92+
v = [x, y]
93+
self.assertEqual(normsq(v), x**2 + y**2)
94+
self.assertEqual(normsq(np.r_[v]), x**2 + y**2)
95+
96+
def test_cross(self):
97+
98+
A = np.eye(3)
99+
100+
for i in range(0, 3):
101+
j = (i + 1) % 3
102+
k = (i + 2) % 3
103+
self.assertTrue(all(cross(A[:,i], A[:,j]) == A[:,k]))
104+
85105
def test_isunittwist(self):
86106
# 3D
87107
# unit rotational twist

‎spatialmath/base/vectors.py‎

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
try: # pragma: no cover
1919
# print('Using SymPy')
20-
from sympy import Matrix
20+
import sympy
2121

2222
_symbolics = True
2323

@@ -122,16 +122,82 @@ def norm(v):
122122
>>> from spatialmath.base import *
123123
>>> norm([3, 4])
124124
125+
.. note:: This function does not use NumPy, it is ~2x faster than
126+
`numpy.linalg.norm()` for a 3-vector
127+
125128
:seealso: :func:`~spatialmath.base.unit`
126129
127130
:SymPy: supported
128131
"""
129-
v = getvector(v)
130-
if v.dtype.kind == 'O':
131-
return Matrix(v).norm()
132+
sum = 0
133+
for x in v:
134+
sum += x * x
135+
136+
if isinstance(sum, sympy.Expr):
137+
return sympy.sqrt(sum)
132138
else:
133-
return np.linalg.norm(v)
139+
return math.sqrt(sum)
140+
141+
def normsq(v):
142+
"""
143+
Squared norm of vector
144+
145+
:param v: any vector
146+
:type v: array_like(n)
147+
:return: norm of vector
148+
:rtype: float
134149
150+
``norm(sq)`` is the sum of squared elements of the vector ``v``
151+
or :math:`|v|^2`.
152+
153+
.. runblock:: pycon
154+
155+
>>> from spatialmath.base import *
156+
>>> normsq([2, 3])
157+
158+
.. note:: This function does not use NumPy, it is ~2x faster than
159+
`numpy.linalg.norm() ** 2` for a 3-vector
160+
161+
:seealso: :func:`~spatialmath.base.unit`
162+
163+
:SymPy: supported
164+
"""
165+
sum = 0
166+
for x in v:
167+
sum += x * x
168+
169+
return sum
170+
171+
def cross(u, v):
172+
"""
173+
Cross product of vectors
174+
175+
:param u: any vector
176+
:type u: array_like(3)
177+
:param v: any vector
178+
:type v: array_like(3)
179+
:return: cross product
180+
:rtype: nd.array(3)
181+
182+
``cross(u, v)`` is the cross product of the vectors ``u`` and ``v``.
183+
184+
.. runblock:: pycon
185+
186+
>>> from spatialmath.base import *
187+
>>> cross([1, 0, 0], [0, 1, 0])
188+
189+
.. note:: This function does not use NumPy, it is ~1.5x faster than
190+
`numpy.cross()`
191+
192+
:seealso: :func:`~spatialmath.base.unit`
193+
194+
:SymPy: supported
195+
"""
196+
return np.r_[
197+
u[1] * v[2] - u[2] * v[1],
198+
u[2] * v[0] - u[0] * v[2],
199+
u[0] * v[1] - u[1] * v[0]
200+
]
135201

136202
def isunitvec(v, tol=10):
137203
"""
@@ -277,9 +343,9 @@ def unittwist(S, tol=10):
277343
Returns None if the twist has zero magnitude
278344
"""
279345

280-
s = getvector(S, 6)
346+
S = getvector(S, 6)
281347

282-
if iszerovec(s, tol=tol):
348+
if iszerovec(S, tol=tol):
283349
return None
284350

285351
v = S[0:3]
@@ -322,9 +388,9 @@ def unittwist_norm(S, tol=10):
322388
.. note:: Returns (None,None) if the twist has zero magnitude
323389
"""
324390

325-
s = getvector(S, 6)
391+
S = getvector(S, 6)
326392

327-
if iszerovec(s, tol=tol):
393+
if iszerovec(S, tol=tol):
328394
return (None, None)
329395

330396
v = S[0:3]
@@ -460,4 +526,4 @@ def removesmall(v, tol=100):
460526
if __name__ == '__main__': # pragma: no cover
461527
import pathlib
462528

463-
exec(open(pathlib.Path(__file__).parent.absolute() / "test_vectors.py").read()) # pylint: disable=exec-used
529+
exec(open(pathlib.Path(__file__).parent.absolute() / "test" / "test_vectors.py").read()) # pylint: disable=exec-used

‎spatialmath/timing.py‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,15 +216,27 @@ def result(op, t):
216216
misc_setup = '''
217217
from spatialmath import base
218218
import numpy as np
219-
s = np.r_[1,2,3,4,5,6]
219+
s = np.r_[1.0,2,3,4,5,6]
220+
a = np.r_[1.0, 2.0, 3.0]
221+
b = np.r_[-5.0, 4.0, 3.0]
222+
220223
'''
221224
table.rule()
222225

226+
t = timeit.timeit(stmt='c = np.cross(a,b)', setup=misc_setup, number=N)
227+
result("np.cross()", t)
228+
229+
t = timeit.timeit(stmt='c = base.cross(a,b)', setup=misc_setup, number=N)
230+
result("cross()", t)
231+
223232
t = timeit.timeit(stmt='a = np.inner(s,s).sum()', setup=misc_setup, number=N)
224233
result("inner()", t)
225234

226235
t = timeit.timeit(stmt='a = np.linalg.norm(s) ** 2', setup=misc_setup, number=N)
227-
result("norm**2", t)
236+
result("np.norm**2", t)
237+
238+
t = timeit.timeit(stmt='a = base.normsq(s)', setup=misc_setup, number=N)
239+
result("base.normsq", t)
228240

229241
t = timeit.timeit(stmt='a = (s ** 2).sum()', setup=misc_setup, number=N)
230242
result("s**2.sum()", t)

0 commit comments

Comments
 (0)