-
Notifications
You must be signed in to change notification settings - Fork 626
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
New adjoint filtering scheme #2012
Comments
This coordinate system looks wrong to me. For FFT-based filtering one has periodic boundary conditions, so ±L/2 are equivalent points and should not be included twice. (Then you subsequently zero-pad, but you do so by equal amounts on both sides, so you have the same problem.) It's pretty easy to get the boundary conditions / origin slightly wrong for this sort of thing, especially in conjunction with padding and |
One option would be to use |
Wouldn't a simpler option just be to symmetrize the filter by adding it to its mirror flip (taking care that mirror flip is not equivalent to |
The kernel is symmetric though. So
Note that we are first building the kernel in the spatial domain, and then running an FFT on that. We are only using the coordinates to build the kernel itself. Would
But if the padding is identical on both sides, it's still "periodic", right? As you say, however, I'm sure there's a bug somewhere w.r.t. the boundary conditions or origin. |
The point is that if the period were Natively in an DFT, a "symmetric" convolution kernel of length If |
Ah this is the issue. Turns out meep/python/adjoint/filters.py Line 139 in 34b62e9
From their docs:
|
In particular, for a filter length
|
By zero-padding, doesn't that mean the new array is no longer symmetric (if the number of taps is even)? For example, suppose
However, as soon as I pad it (
Or am I missing something? |
Yes, right, sorry. You should split the Nyquist (middle) element 50–50 between the positive and negative components when zero-padding an even-length array. i.e. zero-pad with something like: # zero pad by k (before ifftshift)
def symmetric_zeropad(x, k):
y = np.pad(x, (k, k))
if len(x) % 2 == 0: # even
middle = x[0]
y[k] = middle/2
y[-k] = middle/2
return y |
e.g. >>> x = [1,2,3.0,2]
>>> y = symmetric_zeropad(x, 3)
array([0. , 0. , 0. , 0.5, 2. , 3. , 2. , 0.5, 0. , 0. ])
>>> np.fft.ifftshift(y)
array([3. , 2. , 0.5, 0. , 0. , 0. , 0. , 0. , 0.5, 2. ])
>>> np.fft.fft(np.fft.ifftshift(y))
array([8.00000000e+00+0.j, 6.54508497e+00+0.j, 3.42705098e+00+0.j,
9.54915028e-01+0.j, 7.29490169e-02+0.j, 3.33066907e-16+0.j,
7.29490169e-02+0.j, 9.54915028e-01+0.j, 3.42705098e+00+0.j,
6.54508497e+00+0.j]) |
Alternatively, it might be clearer to enforce that the convolution kernel is symmetric by only specifying half of it (from [0,L/2]), and then constructing the other half by mirror flipping (and padding as needed). |
The current filtering scheme often induces a spatial delay in each dimension. This implies the filter kernel is not a "zero-phase" filter. In theory, all of the example impulse responses currently in the codebase (top-hat, conic, and Gaussian) should be zero-phase, indicating our setup is wrong.
One way to get around this is to implement a "filt-filt" (essentially filter in both directions, and remove the phase delay). This is what is implemented in #1625. This, of course, is not what we want, as this changes the underlying kernel of the final filter operation, rendering all of our minimum length-scale constraint math invalid. It should be possible to properly implement a single-pass filter with the above kernels.
Since all of our kernels are separable, we can actually do a batched convolution (natively supported by
autograd
) so long as we construct our filter kernels properly and apply the right padding (to preserve the correct shape).Right now we do an fft-based filter. We may not need to with a batched convolution (depending on the size of the kernel, an fft convolution may be slower).
(cc @hammy4815)
The text was updated successfully, but these errors were encountered: