Skip to content

feat: autograd support for Clip Operations #2450

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

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

rahul-flex
Copy link
Contributor

@rahul-flex rahul-flex commented May 12, 2025

Autograd support for ClipOperation with respect to the vertices and slab_bounds of its constituent PolySlab.

Key Changes:
Differentiable Geometry Representation:
Introduces a "soft membership" function. It provides a smooth, differentiable approximation of the sharp boundaries and non-differentiable nature of boolean geometric operations (intersection, union, difference and symmetric-difference), enabling gradient computation.

Adjoint-Based Derivative Calculation:
Computes gradients using the adjoint method, integrating permittivity sensitivity , and the gradient of the soft membership function wrt polylab params.

Expanded Monitor Bounds:
Adjoint monitors now encompass both input geometries with padding, ensuring accurate field capture for derivative calculations near boundaries.

Validation:
Results verified against finite-difference checks for all parameters and boolean operation types (attached).

Screenshot 2025-05-12 at 6 18 47 PM Screenshot 2025-05-12 at 6 19 14 PM Screenshot 2025-05-12 at 6 19 28 PM Screenshot 2025-05-12 at 6 20 02 PM Screenshot 2025-05-12 at 6 20 36 PM Screenshot 2025-05-12 at 6 20 49 PM Screenshot 2025-05-12 at 6 21 07 PM Screenshot 2025-05-12 at 6 21 23 PM Screenshot 2025-05-12 at 6 36 09 PM Screenshot 2025-05-12 at 6 22 23 PM Screenshot 2025-05-12 at 6 22 50 PM Screenshot 2025-05-12 at 6 34 19 PM

Greptile Summary

Implements automatic differentiation support for ClipOperation geometries, enabling gradient calculations for boolean operations between PolySlabs through differentiable approximations.

  • Added "soft membership" function in tidy3d/components/geometry/base.py to create smooth, differentiable approximations of boolean operations (union, intersection, difference)
  • Expanded monitor bounds in tidy3d/components/structure.py with 5% padding to ensure accurate field capture near boundaries
  • Implemented compute_derivatives() for ClipOperation to calculate gradients with respect to vertices and slab_bounds
  • Added comprehensive test suite in tests/test_components/test_autograd_clip_operations.py validating derivatives against finite-difference checks
  • Removed test_error_clip from tests/test_components/test_autograd.py as ClipOperations now support differentiation

@rahul-flex rahul-flex requested a review from yaugenst-flex May 12, 2025 22:30
Copy link

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

4 files reviewed, 5 comments
Edit PR Review Bot Settings | Greptile

Comment on lines 289 to +321
geometry = self.geometry
box = geometry.bounding_box

# we dont want these fields getting traced by autograd, otherwise it messes stuff up
# Construct bounding box (with padding if ClipOperation)
if isinstance(geometry, td.ClipOperation):
lo_a, hi_a = self._poly_bbox(geometry.geometry_a)
lo_b, hi_b = self._poly_bbox(geometry.geometry_b)

size = [get_static(x) for x in box.size]
center = [get_static(x) for x in box.center]
lo = np.minimum(lo_a, lo_b)
hi = np.maximum(hi_a, hi_b)

# polyslab only needs fields at the midpoint along axis
if (
isinstance(geometry, PolySlab)
and not isinstance(self.medium, AbstractCustomMedium)
and field_keys == [("vertices",)]
):
size[geometry.axis] = 0
lengths = hi - lo # [Lx, Ly, Lz]
pad_vec = 0.05 * lengths # 5 % on every side

lo -= pad_vec
hi += pad_vec

size = (hi - lo).tolist()
center = ((hi + lo) * 0.5).tolist()

else:
# --- original behaviour --------------------------------------
box = geometry.bounding_box
# we dont want these fields getting traced by autograd, otherwise it messes stuff up
size = [get_static(v) for v in box.size]
center = [get_static(v) for v in box.center]
# polyslab only needs fields at the midpoint along axis
if (
isinstance(geometry, PolySlab)
and not isinstance(self.medium, AbstractCustomMedium)
and field_keys == [("vertices",)]
):
size[geometry.axis] = 0.0

Copy link

Choose a reason for hiding this comment

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

style: Complex boolean conditions and branching paths that modify bounding box calculation. Consider refactoring into separate helper methods

Comment on lines +280 to +295
if SAVE_RESULTS:
RESULTS_DIR.mkdir(parents=True, exist_ok=True)
np.save(
RESULTS_DIR / f"fd_ad_{scenario['name'].replace(' ', '_')}"
f"_{operation}_{param_label}.npy",
dict(
scenario_name=scenario["name"],
operation=operation,
param_label=param_label,
fd_val=float(fd_val),
ad_val=float(ad_val),
p_plus=float(p_plus),
p_minus=float(p_minus),
rel_diff=float(rel_diff),
),
)
Copy link

Choose a reason for hiding this comment

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

style: convert this block into a separate helper function for better code organization

Comment on lines +2965 to +2967
def softmin(x, axis=1, lam=30.0):
return -1.0 / lam * logsumexp(-lam * x, axis=axis)

Copy link

Choose a reason for hiding this comment

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

style: Consider adding docstring to describe parameters lam and alpha, since these significantly affect the smoothness of the approximation

Comment on lines +3017 to +3023
def polygon_soft_membership_2d(
verts: np.ndarray,
points: np.ndarray,
alpha: float = 20.0,
beta: float = 5.0,
) -> np.ndarray:
"""Vectorized "soft membership" for polygon 'verts' on 'points'.
Copy link

Choose a reason for hiding this comment

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

style: Add input validation to ensure alpha > 0 and beta > 0 since these control sharpness/smoothness of approximation

Comment on lines +3406 to +3408
mem_fix_a[d] = _membership_3d(verts_a, bounds_a, axis_a, d)
mem_fix_b[d] = _membership_3d(verts_b, bounds_b, axis_b, d)
der_var_a[d], der_var_b[d] = _boolean_coeff(mem_fix_a[d], mem_fix_b[d], self.operation)
Copy link

Choose a reason for hiding this comment

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

style: Extract magic number coefficients like alpha=20.0, beta=5.0 into named class constants with clear descriptions

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.

1 participant