-
Notifications
You must be signed in to change notification settings - Fork 58
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
base: develop
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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
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 | ||
|
There was a problem hiding this comment.
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
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), | ||
), | ||
) |
There was a problem hiding this comment.
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
def softmin(x, axis=1, lam=30.0): | ||
return -1.0 / lam * logsumexp(-lam * x, axis=axis) | ||
|
There was a problem hiding this comment.
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
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'. |
There was a problem hiding this comment.
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
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) |
There was a problem hiding this comment.
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
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).
Greptile Summary
Implements automatic differentiation support for ClipOperation geometries, enabling gradient calculations for boolean operations between PolySlabs through differentiable approximations.
tidy3d/components/geometry/base.py
to create smooth, differentiable approximations of boolean operations (union, intersection, difference)tidy3d/components/structure.py
with 5% padding to ensure accurate field capture near boundariestests/test_components/test_autograd_clip_operations.py
validating derivatives against finite-difference checkstest_error_clip
fromtests/test_components/test_autograd.py
as ClipOperations now support differentiation