-
Notifications
You must be signed in to change notification settings - Fork 4
Difference map implementation and switchable interpolation #20
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
Conversation
that allows you to choose the type of interpolator used by the object's method for extracting patches (can use bilinear, fourier, or nearest).
src/ptychi/forward_models.py
Outdated
| return g | ||
|
|
||
|
|
||
| def replace_propagated_exit_wave_magnitude(psi: Tensor, actual_pattern_intensity: Tensor) -> Tensor: |
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.
Not sure if this should be in ForwardModel. Maybe we can put it in AnalyticalIterativePtychographyReconstructor.
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.
Ok, it is now a static method of AnalyticalIterativePtychographyReconstructor
| ) | ||
|
|
||
| object_denominator = torch.zeros_like(object_.get_slice(0), dtype=object_.data.real.dtype) | ||
| object_denominator = self.patch_placer( |
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.
You can use the existing routine to calculate the object preconditioner, which uses less GPU memory. Add this to DMReconstructor:
def run_pre_epoch_hooks(self) -> None:
self.update_preconditioners()
then you can access the preconditioner here as self.parameter_group.object.preconditioner.
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 just added this, but self.update_preconditioners() had to be put directly before the object update in order for it to work properly
| "add", | ||
| ).real | ||
|
|
||
| updated_object = (object_numerator / (object_denominator + 1e-10))[None] |
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.
1e-10 is too small. The regions not illuminated by the probe can have zero values, and they can make the algorithm unstable. In LSQML this is what we do:
preconditioner = self.parameter_group.object.preconditioner
delta_o_hat = delta_o_hat / torch.sqrt(
preconditioner**2 + (preconditioner.max() * alpha_mix) ** 2
)
alpha_mix is 0.05 by default and it is used in pty-chi and Tike, but PtychoShelves doesn't use it (i.e., their alpha_mix is 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.
I tried using this:
updated_object = object_numerator / torch.sqrt(
object_denominator**2 + (0.05 * object_denominator.max()) ** 2
)and it did prevent the instability when using Fourier interpolation, but the difference map error still oscillates.
The 1e-10 value does work fine for bilinear and nearest interpolation, I think it is because they are more precise than place_patches_fourier_shift.
Anyway, I'll use the new code snippet since it is working with all three of the interpolators.
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 reason why Fourier shift is unstable with 1e-10 is likely that it produces small non-zero values where it should be exactly 0. Bilinear and especially nearest are not more precise in well illuminated areas but they preserve the zeros.
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.
Yes, sorry, I wasn't being precise in my language. That's what I meant.
|
The object update denominator might have contributed to the instability you saw. Could you test it again after changing it to |
stevehenke
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.
Nice work, Hanna!
| from ptychi.metrics import MSELossOfSqrt | ||
| import ptychi.forward_models as fm | ||
|
|
||
| if TYPE_CHECKING: |
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.
Can this conditional be removed in favor of always doing the imports? The quoting business on type hints can be avoided by adding: "from __ future __ import annotations" to the top of the file.
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 ran into circular import issues in quite a few files without using TYPE_CHECKING. With this conditional the intention is to keep the import as unidirectional as possible (API imports from backend but not the other way round).
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.
How about addressing this by creating an issue to (1) identify where the circular import issues are, and (2) refactor to fix the circular imports?
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 looked into this sometime in the past. Most of the circular imports are caused by importing ptychi.api in other modules such as reconstructors, forward model, etc. The API imports are mainly used for (1) type annotation and (2) getting the enums in ptychi.api.enums to use them in conditionals (e.g., if options.noise_model == api.NoiseModels.GAUSSIAN).
(1) can't be helped with quoting or __future__.annotations as long as the imports are run in the global namespace. We could alternatively move the imports inside classes, but not sure that's a good way.
(2) could probably be fixed by moving enums out of api/, but users of the Python API will have to import the enums from somewhere else which I feel isn't intuitive.
I used this TYPE_CHECKING thing because it just prevents the import of api in the backend at runtime, so at runtime we have a clean one-way dependency. I think this also reduces the chance of circular imports for future developments.
src/ptychi/maps.py
Outdated
|
|
||
|
|
||
| def get_patch_interp_function_by_enum( | ||
| key: enums.PatchInterpolationMethods, function_type: Literal["extractor", "placer"] |
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.
Would it be beneficial to use an Enum instead of a Literal for function_type?
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.
function_type is internal and not intended to be used by users, so I think this is fine.
| multislice_regularization_stride: int = 1 | ||
| """The number of epochs between multislice regularization updates.""" | ||
|
|
||
| patch_interpolation_method: enums.PatchInterpolationMethods = enums.PatchInterpolationMethods.FOURIER |
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 is probably a bigger discussion, but when base options are added what should be done to ensure that they are supported by all of the reconstruction algorithms?
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.
Some of the base options are used in the abstract classes of the reconstructors (like IterativePtychographyReconstructor). For this patch_interpolation_method, looks like it sets the function used in PlanarObject.extrract/place_patches. The PlanarObject class is used in all reconstructors, so it will apply to all.
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.
So this actually is an issue. I added the option to switch interpolators because of some issues I was having with the Fourier interpolation method, but that is resolved now. You can use bilinear, nearest, or Fourier interpolation with difference map... but it doesn't work with the ePIE reconstructor (and probably won't with the LSQML reconstructor) without replacing all calls to place_patches_fourier_shift with a call to the output of the get_patch_interp_function_by_enum function. Mixing and matching interpolators doesn't give good results. So I will have to update this.
This new feature won't actually change the default behavior of any of the algorithms since everything is defaulting to using the fourier interpolation anyway. But, it won't be hard to finish this update in case we want to change the interpolators in the future.
…le good reconstructions using fourier interpolation. Changed DM calculation of object denominator to usethe preconditioner. Changed the preconditioner calculation to use any type of interpolator depending on the option.
…other reconstructors) now that it is working properly.
… one specified in object options/
|
It looks good to me now and we can merge it after the conflicts are resolved. Well done! |
…lation functions.
…traction function.
Updates
patch_interpolation_methodthat selects the type of interpolation used to extract object patches inPlanarObject.extract_patches. The default value isfourier. Other options arebilinearandnearest. This setting also determines what kind of interpolation is used in theupdate_objectmethod ofDMReconstructor.Notes/Issues
fourierinterpolation. I did some tests trying different interpolators for the patch extraction and placement, and reconstructions wherefourierinterpolation was used always blew up after a while. Here is a plot showing that:I have more detailed notes on the performance in a powerpoint we can go over sometime.