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

WIP: Vectorize TemporalTransformManager #301

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from

Conversation

JonasHablitzel
Copy link

Hello,

i want to add a feature to the libary to support multiple timestamps for the get_transform_at_time method for the TemporalTransformManager class.

For this i already added a class NumpyTimeseriesTransformTrajectory which is able to take a array as input for the as_matrix method.

Regarding the public facing API i wanted to ask you what you would prefer more. For my point of view there are two options:

Option1

Keep interface the same

manager = TemporalTransformManager()
...
timestamps = np.asarray([10,20,30])
manager .get_transform_at_time("a","b",timestamps )

Option2

add a new method for batch/trajectorie operations.

manager = TemporalTransformManager()
...
timestamps = np.asarray([10,20,30])
manager .get_transform_at_time_batch("a","b",timestamps )

I got Option1 kinda to work with the following addition.

class TemporalTransformManagerVectorized(TemporalTransformManager):
    def _path_transform(self, path):
        """Convert sequence of node names to rigid transformation."""
        A2B = np.eye(4)
        for from_f, to_f in zip(path[:-1], path[1:]):
            A2B = concat_dynamic(
                A2B,
                self.get_transform(from_f, to_f),
            )
        return A2B

@AlexanderFabisch
Copy link
Member

Hi @JonasHablitzel ,

in my opinion it is not necessary to introduce another subclass of TimeVaryingTransform that behaves differently from the others when you actually want to modify the interface of the TemporalTransformManager. I would be in favor of modifying the existing NumpyTimeseriesTransform that already stores a trajectory. It should be possible to vectorize the current implementation.

I would be a little bit more in favor of get_transform_at_time("a", "b", timestamps) vs. get_transform_at_time_batch("a", "b", timestamps) as it is a bit more in line with, e.g., the numpy ufuncs.

I would like to involve @kopytjuk who implemented the TemporalTransformManager (if you are available).

@@ -104,9 +103,11 @@ def check_transforms(self):
return self

def _interpolate_pq_using_sclerp(self, query_time):
if isinstance(query_time,float) or isinstance(query_time,float):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to adapt typings here as well since we allow both floats and arrays

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this line does not really make sense, right? Checks the same thing twice. It is also not very general. What if an int is passed? Or float32?

Copy link
Contributor

@kopytjuk kopytjuk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First of all @JonasHablitzel, thank you for your contribution!

I believe making the transform lookup vectorized is a great addition to the code, for many real world projects looping over the timesteps and calling the existing get_transform_at_time() is indeed quite slow ...

@@ -504,6 +504,47 @@ def test_numpy_timeseries_transform():
assert origin_of_A_in_B_y == pytest.approx(-1.28, abs=1e-2)


def test_numpy_timeseries_transform_multiple_timestamps_query():
Copy link
Contributor

@kopytjuk kopytjuk Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since pytest is used, consider extending the existing test function using pytest.mark.parametrize:

@pytest.mark.paramertrize("query_times", [4.9, np.array([4.9, 4.9])]
def test_numpy_timeseries_transform(query_times):
    # ...
    tm.get_transform_at_time("A", "B", query_times)

Having done that, we can have a single test, testing both the scalar and array inputs.

A2W_at_start_2 = tm.get_transform_at_time("A", "W", query_time)
assert_array_almost_equal(A2W_at_start, A2W_at_start_2, decimal=2)

query_times = [4.9,4.9] # [s]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds quite a lot vectorized mathematical operations, which IMHO need a little more unittests than testing with an array of two equal values ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting with the nitpicking here: please also check for PEP8 with either pep8 or flake8.

Comment on lines +142 to +143
def concat_dynamic(A2B, B2C):
"""Concatenate multiple transformations A2B with transformation B2C.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify what "dynamic" means:

Suggested change
def concat_dynamic(A2B, B2C):
"""Concatenate multiple transformations A2B with transformation B2C.
def concat_dynamic(A2B, B2C):
"""Concatenate multiple transformations A2B with transformation B2C.
This function accepts different numbers or shapes of the input transformations, e.g. 4x4 or Nx4x4 shape.



non_zero_mask = ~zero_mask
res[non_zero_mask, :3] = a[non_zero_mask, :3] / norm[non_zero_mask, None]
Copy link
Contributor

@kopytjuk kopytjuk Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the norm is one-dimensional after you applied np.linalg.norm on a 2D (Nx4) matrix:

Suggested change
res[non_zero_mask, :3] = a[non_zero_mask, :3] / norm[non_zero_mask, None]
res[non_zero_mask, :3] = a[non_zero_mask, :3] / norm[non_zero_mask]


return qs, s_axis, hs, thetas

def dual_quaternions_from_screw_parameters(qs, s_axis, hs, thetas):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this function is non-trivial when it comes to mathematical operations, would you please add at least a unittest so that a next developer wants to optimize/change/accelerate could check her work?

return result


def screw_parameters_from_dual_quaternions(dqs):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this function is non-trivial when it comes to mathematical operations, would you please add at least a unittest so that a next developer wants to optimize/change/accelerate could check her work?

@kopytjuk
Copy link
Contributor

kopytjuk commented Oct 24, 2024

The more I look deeper into this contribution, the more I appreciate the work.

I think a good way forward would be to:

  1. Adapt the TemporalTransformManager to have an array of timestamps as self._current_time (instead of a scalar timestamp as it is now).
  2. Adapt the interface of the TimeVaryingTransform.as_matrix(query_time). It should accept time arrays of size N as well and return Nx4x4 tensor. @JonasHablitzel already kind of addressed that in the NumpyTimeseriesTransform.
  3. Having that, we can use the proposed adaptation of _path_transform with the concat_dynamic directly in the TransformGraphBase.

What do you think @AlexanderFabisch on the plan?

@AlexanderFabisch
Copy link
Member

AlexanderFabisch commented Oct 25, 2024

What do you think @AlexanderFabisch on the plan?

Sounds good to me. And thanks for reviewing the PR!

@AlexanderFabisch AlexanderFabisch changed the title added a trajetorie mode for Temporal Vectorize TemporalTransformManager Oct 25, 2024
@AlexanderFabisch AlexanderFabisch changed the title Vectorize TemporalTransformManager WIP: Vectorize TemporalTransformManager Oct 25, 2024
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

Successfully merging this pull request may close these issues.

4 participants