Skip to content

Commit ca09f0c

Browse files
authored
Optimize vec_transform (#102)
* optimize vec_transform * bump version
1 parent f162fcb commit ca09f0c

File tree

3 files changed

+49
-11
lines changed

3 files changed

+49
-11
lines changed

pylinalg/vector.py

+19-9
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ def vec_homogeneous(vectors, /, *, w=1, out=None, dtype=None) -> np.ndarray:
7272
return out
7373

7474

75-
def vec_transform(vectors, matrix, /, *, w=1, out=None, dtype=None) -> np.ndarray:
75+
def vec_transform(
76+
vectors, matrix, /, *, w=1, projection=True, out=None, dtype=None
77+
) -> np.ndarray:
7678
"""
7779
Apply a transformation matrix to a vector.
7880
@@ -86,6 +88,9 @@ def vec_transform(vectors, matrix, /, *, w=1, out=None, dtype=None) -> np.ndarra
8688
The value of the scale component of the homogeneous coordinate. This
8789
affects the result of translation transforms. use 0 (vectors) if the
8890
translation component should not be applied, 1 (positions) otherwise.
91+
projection : bool, optional
92+
If False, the matrix is assumed to be purely affine
93+
and the homogeneous component is not applied. Default is True.
8994
out : ndarray, optional
9095
A location into which the result is stored. If provided, it must have a
9196
shape that the inputs broadcast to. If not provided or None, a
@@ -102,17 +107,22 @@ def vec_transform(vectors, matrix, /, *, w=1, out=None, dtype=None) -> np.ndarra
102107

103108
matrix = np.asarray(matrix)
104109

105-
vectors = vec_homogeneous(vectors, w=w, dtype=float)
106-
result = matrix @ vectors[..., None]
107-
result /= result[..., -1, :][..., None, :]
108-
result = result[..., :-1, 0]
109-
110-
if out is not None:
111-
out[:] = result
110+
if projection:
111+
vectors = vec_homogeneous(vectors, w=w, dtype=float)
112+
vectors @= matrix.T
113+
vectors[..., :-1] /= vectors[..., -1, None]
114+
vectors = vectors[..., :-1]
112115
else:
113-
out = result
116+
vectors = np.asarray(vectors, dtype=float, copy=True)
117+
vectors @= matrix[:-1, :-1].T
118+
vectors += matrix[:-1, -1]
119+
120+
if out is None:
121+
out = vectors
114122
if dtype is not None:
115123
out = out.astype(dtype, copy=False)
124+
else:
125+
out[:] = vectors
116126

117127
return out
118128

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[project]
44
name = "pylinalg"
5-
version = "0.6.4"
5+
version = "0.6.5"
66
description = "Linear algebra utilities for Python"
77
readme = "README.md"
88
license = { file = "LICENSE" }

tests/test_vectors.py

+29-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def test_vector_apply_translation():
6767
vectors = np.array([[1, 0, 0]])
6868
expected = np.array([[0, 2, 2]])
6969
matrix = la.mat_from_translation([-1, 2, 2])
70-
result = la.vec_transform(vectors, matrix)
70+
result = la.vec_transform(vectors, matrix, projection=False)
7171

7272
npt.assert_array_almost_equal(
7373
result,
@@ -92,6 +92,34 @@ def test_vec_transform_out():
9292
assert result is out
9393

9494

95+
def test_vec_transform_projection_flag():
96+
vectors = np.array(
97+
[
98+
[1, 0, 0],
99+
[1, 2, 3],
100+
[1, 1, 1],
101+
[0, 0, 0],
102+
[7, 8, -9],
103+
],
104+
dtype="f4",
105+
)
106+
translation = np.array([-1, 2, 2], dtype=float)
107+
expected = vectors + translation[None, :]
108+
109+
matrix = la.mat_from_translation(translation)
110+
111+
for projection in [True, False]:
112+
for batch in [True, False]:
113+
if batch:
114+
vectors_in = vectors
115+
expected_out = expected
116+
else:
117+
vectors_in = vectors[0]
118+
expected_out = expected[0]
119+
result = la.vec_transform(vectors_in, matrix, projection=projection)
120+
npt.assert_array_equal(result, expected_out)
121+
122+
95123
@given(ct.test_spherical, none())
96124
@example((1, 0, np.pi / 2), (0, 0, 1))
97125
@example((1, np.pi / 2, np.pi / 2), (1, 0, 0))

0 commit comments

Comments
 (0)