-
Notifications
You must be signed in to change notification settings - Fork 633
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
Fix incorrect backward EigenmodeCoefficient
gradients and update test case
#1593
Fix incorrect backward EigenmodeCoefficient
gradients and update test case
#1593
Conversation
As a starting point, it seems that the fix suggested in #1585 of adding: kpoint_func=lambda a,b: mp.Vector3(x=-1) to the constructor of |
b7dacf3
to
4ea9ee5
Compare
The issue described in #1585 should be fixed with this. With this PR, I am proposing a slight refactoring where a private |
EigenmodeCoefficient
gradients and update test case
Rather than make the hack more elegant, why not focus on fixing the actual bug? |
Is there some reason that it's not advisable to define the forward orientation as being along the negative direction of an axis? In any case, this approach at least leads to correct gradients until someone is able to look into this more. |
python/adjoint/objective.py
Outdated
if self._monitor.normal_direction == 0: | ||
direction_vector = mp.Vector3(x=1) | ||
elif self._monitor.normal_direction == 1: | ||
direction_vector = mp.Vector3(y=1) | ||
elif self._monitor.normal_direction == 2: | ||
direction_vector = mp.Vector3(z=1) |
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.
Does this preclude us from passing arbitrary kpoint
s for oblique waveguides?
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.
I've reintroduced the kpoint_func
param. WDYT?
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.
Seems right from a high level -- thanks for looking into this for me.
Have you created a test for it by chance? Or are all the tests just forward/backward (or equally up/down)?
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.
The test case updated in this PR checks forward and backward gradients via the forward
parameter of EigenmodeCoefficient
, but does not have a case where kpoint_func
is specified. I've personally not used the oblique waveguide capability but if you have something in mind let me know.
In theory, no, but if there's a bug causing the issue we should probably fix the bug. Especially since it might lead to fixing other issues. I'm a little worried this approach actually limits the user (i.e. me) from defining arbitrary modes (e.g. diffraction modes, oblique waveguide modes, etc.) |
Sure, I agree, but the concern here is that incorrect results are being produced... so we should fix that first.
Are you saying that you want the ability override the |
I've reintroduced the |
2646e70
to
7a540be
Compare
python/adjoint/objective.py
Outdated
kpoint_func = self.kpoint_func | ||
overlap_idx = self.kpoint_func_overlap_idx | ||
else: | ||
kpoint_func = lambda *not_used: self._direction_vector |
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.
This doesn't seem right — the k point is dimensionful, so the default guess is scaled by fcen*(refractive index)
.
For comparison, this is the default guess in mpb.cpp
:
Line 487 in 8318114
kmatch = frequency * sqrt(real(get_eps(cen, frequency)) * real(get_mu(cen, frequency))); |
python/adjoint/objective.py
Outdated
if self.kpoint_func: | ||
eig_kpoint = self.kpoint_func(time_src.frequency, self.mode) | ||
else: | ||
eig_kpoint = self._direction_vector |
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.
This isn't quite the right guess, since it has the wrong units. It would be better to use mpb.cpp's guess for this, which is obtained by passing (0.0,0.0,0.0)
for the k-point. To tell it to flip the sign, we could pass (-0.0,-0.0,-0.0)
and use the sign of zero:
diff --git a/src/mpb.cpp b/src/mpb.cpp
index c41e3c2a..5a68ca7b 100644
--- a/src/mpb.cpp
+++ b/src/mpb.cpp
@@ -484,7 +484,7 @@ void *fields::get_eigenmode(double frequency, direction d, const volume where, c
// which we automatically pick if kmatch == 0.
if (match_frequency && kmatch == 0) {
vec cen = eig_vol.center();
- kmatch = frequency * sqrt(real(get_eps(cen, frequency)) * real(get_mu(cen, frequency)));
+ kmatch = copysign(frequency * sqrt(real(get_eps(cen, frequency)) * real(get_mu(cen, frequency))), kmatch);
if (d == NO_DIRECTION) {
for (int i = 0; i < 3; ++i)
k[i] = dot_product(R[i], kdir) * kmatch; // kdir*kmatch in reciprocal basis
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.
Looks like the sign is being lost in kmatch, so you'd have to look at _kpoint.in_direction(d)
as the second argument of copysign
7a540be
to
0e34127
Compare
Okay, following up on this... it seems that the |
Maybe it is easier to just pass a boolean flag. |
I agree. In the future it would probably be ideal to add a boolean flag, rather than having the user pass +/- zero, to specify the direction in MPB.
I can confirm that the suggestion above in #1593 (comment) successfully sets the sign of I noticed that when Any other thoughts? I would be in favor of using my original approach (passing a vector scaled by the frequency to specify the direction) in the near term if this is going to require a bunch of hacking in the lower level MPB code. I realize that there are some edge cases around users picking potentially weird materials or different units, but right now the adjoint module is producing incorrect gradients and I think that is probably a bigger issue. |
Hmm, looking at the code it looks like the reason might be that it is then computing the group velocity in the wrong direction, since Line 564 in f528873
Maybe it would be better to flip the sign of if (std::signbit(_kpoint.in_direction(d)))
kdir[d - X] = -1; // -0.0 was passed, flip k direction |
kdir[d - X] = -1; This (assignment of a negative value) seems to cause a SIGSEGV error. |
Using
The line causing the segmentation fault is Line 814 in f528873
But what is really causing the segfault is this line in _KPOINT_NEG_DIR = mp.Vector3(-0.0, 0.0, 0.0) Changing this manually to |
28757c1
to
d3abd81
Compare
src/mpb.cpp
Outdated
@@ -485,6 +485,8 @@ void *fields::get_eigenmode(double frequency, direction d, const volume where, c | |||
if (match_frequency && kmatch == 0) { | |||
vec cen = eig_vol.center(); | |||
kmatch = frequency * sqrt(real(get_eps(cen, frequency)) * real(get_mu(cen, frequency))); | |||
if (std::signbit(kpoint.in_direction(d))) | |||
kdir[d - X] = -1; // -0.0 was passed, flip k direction |
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.
Instead, try putting this before the kdir[d - X] = 1;
line
d3abd81
to
2d46ab8
Compare
This is still failing on the check of the gradients in the backward direction using the suggested approach of flipping the sign of I was previously able to get the gradients to be correct in both directions only when |
Actually, there is a more fundamental problem, it seems. If you look at the principle of equivalence, which is described in this line (which comes from equation 4.4 in this paper, then if you're trying to make a backwards mode we should really flip the sign of nhat. Maybe, before this line, add a line
|
2d46ab8
to
08d3e7f
Compare
This is still failing (currently along both directions) with the most recently suggested approach of changing the sign of
These changes are all pushed if you want to take a look and confirm that I'm not doing something unintended. As I mentioned above, I was only able to obtain correct gradients when using |
Would be good to visualize the mode that is being launched here, to verify that it is going in the backward direction, or if fundamentally the mode that is being launched is incorrect. |
Switching the source to: mp.EigenModeSource(
mp.GaussianSource(frequency=fmean, fwidth=fmean * gaussian_rel_width),
eig_band=1,
direction=mp.AUTOMATIC,
size=mp.Vector3(0, wg_width + 2 * wg_padding, 0),
center=[sx / 2 - pml_width - source_to_pml, 0, 0],
), and turning verbosity on for the MPB eigenmode solver produces:
The field profiles do show a backward-propagating mode but the phase of Ez has shifted by π. edit The Hz field is tiny compared to the Ez:
|
Two things:
|
7910d73
to
ee7e823
Compare
… test to check gradients in both forward and backward directions Flip sign of `amp` based on group velocity direction Original fix
ee7e823
to
a579d4d
Compare
I've reverted this PR to use my original approach, which was to pass an appropriate signed unit vector as the k-point to MPB when defining the source and when evaluating the eigenmode coefficients. As @stevengj pointed out above, this approach has the disadvantage of not being scaled correctly / not having the appropriate units. However, I personally believe this is an acceptable trade off in order to fix the more immediate issue with the gradients being incorrect. In practice, MPB usually finds the correct mode. Perhaps we can fork off a separate issue to focus on the work of figuring out a more long term solution with what's going on down in the lower level MPB code. WDYT? |
Codecov ReportAttention: Patch coverage is
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## master #1593 +/- ##
==========================================
+ Coverage 74.39% 74.42% +0.02%
==========================================
Files 13 13
Lines 4581 4586 +5
==========================================
+ Hits 3408 3413 +5
Misses 1173 1173
|
Test failure looks relevant:
|
The failing test occurs for only the single-precision floating point case. We can just probably lower the tolerance ( |
…cient creation comprehension in test, fix design resolution calculation in test
That test seems to be flaky. |
…st case (NanoComp#1593) * Fix backward gradients in EigenmodeCoefficient and update JAX adjoint test to check gradients in both forward and backward directions Flip sign of `amp` based on group velocity direction Original fix * Scale kpoint by abs(center_frequency) and apply yapf formatting * Fix parenthesis in center frequency calculation, fix eigenmode coefficient creation comprehension in test, fix design resolution calculation in test
This PR continues from #1592 to update the test for the Meep-JAX wrapper to check the gradients from
mpa.EigenmodeCoefficient
s withforward=False
. In particular, this PR extends the straight waveguide test case with an additional source on the RHS of the waveguide and with two additionalmpa.EigenmodeCoefficient
s such that both the forward and backward traveling waves on each side of the waveguide are monitored and can be included in the loss function.Currently, the cases that check the gradient of the transmission from right to left are failing (selected via
excite_port_idx
param of the test case), presumably because of #1585. Note that another option for exposing the issue described in #1585 is to check the gradient of reflection from the straight waveguide against finite differences (although this quantity should be close to zero).