Skip to content

Add tests for gradient check of affine transformations #1

New issue

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

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

Already on GitHub? # to your account

Merged
merged 5 commits into from
Jan 15, 2025

Conversation

rahul-flex
Copy link
Owner

  1. Removed @cached_property from [transformations](https://github.com/rahul-flex/tidy3d/blob/rahul-flex/diff-transformations/tidy3d/components/transformation.py#L107-L131) file so that the autograd can dynamically update values. This fixed the gradient issue. However, created errors:
    1. Running pytest test_autograd.py caused errors:
      This created issue as we don't want @cached_property, then we must explicitly call matrix() when using it. Same goes for self.isidentity(). Made the fixes in [/geometry/base](https://github.com/rahul-flex/tidy3d/blob/rahul-flex/diff-transformations/tidy3d/components/geometry/base.py#L2795C1-L2795C78) file.

    2. Now after running all the tests, got 9 errors:

      1. Errors image attached:
errors_pytest
3. Fix:
    1. Converted matrix call to explicit method/func matrix() call. Same for isidentity method.

Copy link
Collaborator

@yaugenst-flex yaugenst-flex left a comment

Choose a reason for hiding this comment

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

Getting there! I noticed some weird formatting in these files, did you install the pre-commit hook and did you run the autoformatter (ruff) on your code before committing? There is some weird indentation, I'm actually surprised it runs at all.

Regarding comments and docstrings: This is already pretty good! Note though that the lowest level of indentation should be aligned with the quotes, i.e. instead of

"""
  docstring
"""

it should rather look like

"""
docstring
"""

Also for tests, it is generally enough if there is only a short docstring that explains what the test does, a full docstring like in the official docs with all parameters and types is generally not necessary (not bad either though).

@@ -21,6 +21,7 @@
from tidy3d.components.autograd.utils import is_tidy_box
from tidy3d.components.data.data_array import DataArray
from tidy3d.plugins.polyslab import ComplexPolySlab
import tidy3d.components.geometry.polyslab as polyslab
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need for this import, polyslab is available as td.PolySlab

Comment on lines 1876 to 1890
@pytest.fixture
def create_polyslab()-> polyslab.PolySlab:
"""
Creates a PolySlab instance for testing affine transformations.

Returns
-------
polyslab.PolySlab
An instance of PolySlab with predefined vertices, axis, and slab bounds.
"""
vertices = np.array([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]])
axis = 1 # 0 for x, 1 for y, 2 for z
slab_bounds = (-1.0, 1.0)

return polyslab.PolySlab(vertices=vertices, axis=axis, slab_bounds=slab_bounds)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Good use of a fixture!

Copy link
Collaborator

Choose a reason for hiding this comment

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

Avoid the use of verbs like create_ in fixture names though, since they will be used implicitly as arguments. So just polyslab would be good here.

create_polyslab : polyslab.PolySlab
The PolySlab object created using pytest fixture.
"""
poly = create_polyslab
Copy link
Collaborator

Choose a reason for hiding this comment

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

Related to fixture name, this is a bit awkward.

The PolySlab object created using pytest fixture.
"""
poly = create_polyslab
x, y, z = 0.1, 0.2, 0.3
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ideally this test would be parametrized with a few different values for x, y, z, specifically to check edge cases, see here for reference.

new_poly = poly.translated(x, y, z)
return new_poly.vertices

for name in ["x", "y", "z"]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

There is no need for the for loop here. Just explicitly check each case.

@@ -464,7 +464,7 @@ def test_rotation():
axis = np.random.random(3)
rot = td.RotationAroundAxis(axis=tuple(axis), angle=1.23)

R = rot.matrix
R = rot.matrix()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah this is not ok, you are changing the behavior of tidy3d classes globally, i.e. you are changing the interface. Do not modify existing tests to make them pass. Fix your code instead so that they pass ;)
The tests are there for a reason.

@@ -2792,7 +2792,7 @@ def rotation(angle: float, axis: Union[Axis, Coordinate]) -> MatrixReal4x4:
Transform matrix with shape (4, 4).
"""
transform = np.eye(4)
transform[:3, :3] = RotationAroundAxis(angle=angle, axis=axis).matrix
transform[:3, :3] = RotationAroundAxis(angle=angle, axis=axis).matrix()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here.

Comment on lines 21 to 26
@abstractmethod
def matrix(self) -> TensorReal:
"""Rotation matrix."""

@cached_property
@abstractmethod
def isidentity(self) -> bool:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Even if you remove the caching, these still need to be properties.

def isidentity(self) -> bool:
"""Check whether rotation is identity."""

return np.isclose(self.angle % (2 * np.pi), 0)

@cached_property

Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here, need to stay properties.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is starting to look pretty good :)
One thing regarding test naming: Try to make the names of your tests mirror what they are testing, this is generally an unspoken convention. So instead of test_translation_grad, name it test_translated_grad since you are testing PolySlab.translated. In the same vein, we are actually only testing PolySlab here. What if we start testing Box and Cylinder too? It'll be cleaner to make the test name more specific then, e.g. test_polyslab_translated_grad, etc.

Copy link
Collaborator

@yaugenst-flex yaugenst-flex left a comment

Choose a reason for hiding this comment

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

This is looking great! Only had some minor comments.

[
(0.0, 0.0, 0.0, True), # No scaling
(0.1, 0.2, 0.3, False), # Small positive values
(-0.1, -0.2, -0.3, True), # Small negative values
Copy link
Collaborator

Choose a reason for hiding this comment

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

After discussing this here actually this should be equivalent to a reflection. You can see for example in this test, negative scaling is tested and expected to work, so this should also work under autograd.

Comment on lines 1944 to 1948
(0.0, 0, True), # No rotation around x-axis
(np.pi / 6, 1, False), # Small rotation around y-axis
(-np.pi / 4, 2, True), # Rotation around z-axis
(np.pi / 4, 1, False), # 90-degree rotation around y-axis
(np.pi, 1, False), # 180-degree rotation around y-axis
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this really only comes down to whether or not we are rotating around the polyslab's axis, so might be better to just check whether we are rotating around that or not?

Comment on lines 1960 to 1961
if not hasattr(new_poly, "vertices"):
raise ValueError("Rotation resulted in a non-differentiable Transformed object.")
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this is not necessary, since check_grads will raise an exception if differentiating through Transformed, so no need to raise one here.

Comment on lines +124 to +125
K = np.array([[0, -n[2], n[1]], [n[2], 0, -n[0]], [-n[1], n[0], 0]])
R = np.eye(3) + s * K + (1 - c) * K @ K
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice! This is much cleaner than before.

@yaugenst-flex yaugenst-flex self-requested a review January 15, 2025 12:25
Copy link
Collaborator

@yaugenst-flex yaugenst-flex left a comment

Choose a reason for hiding this comment

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

Ok overall this is looking great! I think we can try to move this into the main repo.

@rahul-flex rahul-flex merged commit e16845a into pre-2.8-branch Jan 15, 2025
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants