-
Notifications
You must be signed in to change notification settings - Fork 67
feat(tidy3d): FXC-3296-autograd-support-for-anisotropic-medium-and-custom-anisotropic-medium #3080
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
eb1d806 to
9a1d670
Compare
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, no comments
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, 1 comment
9a1d670 to
493bef5
Compare
Diff CoverageDiff: origin/develop...HEAD, staged and unstaged changes
Summary
tidy3d/components/autograd/derivative_utils.pyLines 734-742 734 if the requested map is unavailable.
735 """
736 field_map = {"E": self.E_der_map, "D": self.D_der_map}.get(field_type)
737 if field_map is None:
! 738 raise ValueError("field type must be 'D' or 'E'.")
739
740 axis = axis.lower()
741 projected = dict(field_map)
742 for dim in "xyz":tidy3d/components/medium.pyLines 5984-5992 5984 component_paths = [
5985 tuple(path[1:]) for path in derivative_info.paths if path and path[0] == component
5986 ]
5987 if not component_paths:
! 5988 return None
5989
5990 axis = component[0] # f.e. xx -> x
5991 projected_E = derivative_info.project_der_map_to_axis(axis, "E")
5992 projected_D = derivative_info.project_der_map_to_axis(axis, "D")Lines 5999-6007 5999
6000 components = self.components
6001 for field_path in derivative_info.paths:
6002 if not field_path or field_path[0] not in components:
! 6003 raise NotImplementedError(
6004 f"No derivative defined for '{type(self).__name__}' field: {field_path}."
6005 )
6006
6007 vjps: AutogradFieldMap = {}Lines 6009-6017 6009 comp_info = self._component_derivative_info(
6010 derivative_info=derivative_info, component=comp_name
6011 )
6012 if comp_info is None:
! 6013 continue
6014 comp_vjps = component._compute_derivatives(comp_info)
6015 for sub_path, value in comp_vjps.items():
6016 vjps[(comp_name, *sub_path)] = value |
tidy3d/components/medium.py
Outdated
|
|
||
| # --- shared autograd helpers --- | ||
| @staticmethod | ||
| def _project_field_map_to_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.
could this possibly be something that would fit in data_array as a helper method there. I'm thinking it might fit more with that than as a static method in medium
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.
Right, it is definitely misplaced here. But as it operates on dict[str, FieldData] and could also be applied to D fields, I would argue it should be moved to derivative info.
Added this to DerivativeInfo:
def project_der_map_to_axis(
self, axis: xyz, field_type: str = "E"
) -> dict[str, ScalarFieldDataArray] | None:
"""Return a copy of the selected derivative map with only one axis kept.
Parameters
----------
axis:
Axis to keep (``"x"``, ``"y"``, ``"z"``, case-insensitive).
field_type:
Map selector: ``"E"`` (``self.E_der_map``) or ``"D"`` (``self.D_der_map``).
Returns
-------
dict[str, ScalarFieldDataArray] | None
Copied map where non-selected components are replaced by zeros, or ``None``
if the requested map is unavailable.
"""
field_map = {"E": self.E_der_map, "D": self.D_der_map}.get(field_type)
if field_map is None:
raise ValueError("field type must be 'D' or 'E'.")
axis = axis.lower()
projected = dict(field_map)
for dim in "xyz":
key = field_type + dim
if key not in field_map:
continue
if dim != axis:
projected[key] = _zeros_like(field_map[key])
else:
projected[key] = field_map[key]
return projected
groberts-flex
left a comment
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.
thanks for the addition! overall, looks like a good approach @marcorudolphflex
left a couple comments/questions!
493bef5 to
9cecb5d
Compare
groberts-flex
left a comment
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.
overall, looks good! do you have plots for the outputs of the numerical tests you could share as well?
…stom-anisotropic-medium
9cecb5d to
b001067
Compare
|
thanks for putting those plots together, they look interesting! Talking with Yannick a little bit this morning in our 1:1 and he was saying there is a test case he has for isotropic custom medium that is not working well for autograd. I think it's likely the underlying problem is in there, but might be good to get to the bottom of that. I think he is going to share that one with me and I'll have a look at it to see if I can spot anything. I'll also re-run my other numerical custom medium tests to see if maybe we had a regression somewhere. |







Greptile Overview
Greptile Summary
This PR adds autograd support for diagonal
AnisotropicMediumandCustomAnisotropicMedium, enabling gradient-based inverse design with anisotropic materials. The implementation delegates derivative computation to individual component media (xx, yy, zz) with proper field projection to ensure only the corresponding axis contributes to each gradient.Key changes:
_compute_derivativesmethod inAnisotropicMediumthat projects electric field maps to individual axes and delegates to component media_project_field_map_to_axisand_component_derivative_infofor field filteringThe implementation correctly inherits to
CustomAnisotropicMediumthrough the class hierarchy. Error handling raisesNotImplementedErrorfor unsupported paths (e.g., off-diagonal components), though explicit test coverage for this error case would strengthen the validation.Confidence Score: 4/5
Important Files Changed
File Analysis
_compute_derivativesmethod forAnisotropicMediumthat delegates to component media with proper field projection - minor test coverage gap for error casesAnisotropicMediumandCustomAnisotropicMediumSequence Diagram
sequenceDiagram participant User as User Code participant AAM as AnisotropicMedium participant Comp as Component Medium participant DI as DerivativeInfo User->>AAM: _compute_derivatives(derivative_info) AAM->>AAM: validate all paths exist in components loop for each component (xx, yy, zz) AAM->>AAM: _component_derivative_info(derivative_info, component) AAM->>AAM: _project_field_map_to_axis(E_der_map, axis) Note over AAM: Zero out non-matching field components<br/>(e.g., for xx: keep Ex, zero Ey, Ez) AAM->>DI: create filtered DerivativeInfo AAM->>Comp: component._compute_derivatives(comp_info) Comp-->>AAM: component gradients AAM->>AAM: prepend component name to gradient keys end AAM-->>User: combined gradients for all componentsContext used:
dashboard- Add tests for all public constructors and methods to confirm expected behavior, including effects on... (source)dashboard- Prefer pytest.mark.parametrize over manual for loops to define and run distinct test cases, reducing... (source)