-
Notifications
You must be signed in to change notification settings - Fork 19
test: adding a test to unsqueeze squeezed data #180
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
Changes from 6 commits
9a53b24
ac388e9
ce863d3
ca69295
a1b6425
14d92cb
a48ce43
d42d234
f47e167
4933b4f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| **Added:** | ||
|
|
||
| * Polynomial squeeze of x-axis of morphed data | ||
|
|
||
| **Changed:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Deprecated:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Removed:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Fixed:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Security:** | ||
|
|
||
| * <news item> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import numpy as np | ||
| from numpy.polynomial import Polynomial | ||
| from scipy.interpolate import interp1d | ||
|
|
||
| from diffpy.morph.morphs.morph import LABEL_GR, LABEL_RA, Morph | ||
|
|
||
|
|
||
| class MorphSqueeze(Morph): | ||
| """Squeeze the morph function. | ||
|
|
||
| This applies a polynomial to squeeze the morph non-linearly. | ||
|
|
||
| Configuration Variables | ||
| ----------------------- | ||
| squeeze | ||
| list or array-like | ||
| Polynomial coefficients [a0, a1, ..., an] for the squeeze function. | ||
| """ | ||
|
|
||
| # Define input output types | ||
| summary = "Squeeze morph by polynomial shift" | ||
| xinlabel = LABEL_RA | ||
| yinlabel = LABEL_GR | ||
| xoutlabel = LABEL_RA | ||
| youtlabel = LABEL_GR | ||
| parnames = ["squeeze"] | ||
|
|
||
| def morph(self, x_morph, y_morph, x_target, y_target): | ||
| Morph.morph(self, x_morph, y_morph, x_target, y_target) | ||
| if self.squeeze is None or np.allclose(self.squeeze, 0): | ||
| self.x_morph_out = self.x_morph_in | ||
| self.y_morph_out = self.y_morph_in | ||
| return self.xyallout | ||
|
|
||
| squeeze_polynomial = Polynomial(self.squeeze) | ||
| x_squeezed = self.x_morph_in + squeeze_polynomial(self.x_morph_in) | ||
|
|
||
| self.y_morph_out = interp1d( | ||
| x_squeezed, self.y_morph_in, kind="cubic", bounds_error=False | ||
| )(self.x_morph_in) | ||
|
|
||
| self.x_morph_out = self.x_morph_in | ||
| return self.xyallout |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| import numpy as np | ||
| import pytest | ||
| from numpy.polynomial import Polynomial | ||
|
|
||
| from diffpy.morph.morphs.morphsqueeze import MorphSqueeze | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| "squeeze_coeffs", | ||
| [ | ||
| # The order of coefficients is [a0, a1, a2, ..., an] | ||
| # Negative cubic squeeze coefficients | ||
| [-0.2, -0.01, -0.001, -0.001], | ||
| # Positive cubic squeeze coefficients | ||
| [0.2, 0.01, 0.001, 0.001], | ||
| # Positive and negative cubic squeeze coefficients | ||
| [0.2, -0.01, 0.002, -0.001], | ||
| # Quadratic squeeze coefficients | ||
| [-0.2, 0.005, -0.007], | ||
| # Linear squeeze coefficients | ||
| [0.1, 0.3], | ||
| # 4th order squeeze coefficients | ||
| [0.2, -0.01, 0.001, -0.001, 0.0004], | ||
| # Zeros and non-zeros, expect 0 + a1x + 0 + a3x**3 | ||
| [0, 0.03, 0, -0.001], | ||
| # Testing zeros, expect no squeezing | ||
| [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], | ||
| ], | ||
| ) | ||
| def test_morphsqueeze(squeeze_coeffs): | ||
| x_target = np.linspace(0, 10, 1001) | ||
| y_target = np.sin(x_target) | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. close up. no blank line here (and everywhere below) |
||
| x_make = np.linspace(-3, 13, 1601) | ||
| lower_idx = np.where(x_make == 0.0)[0][0] | ||
| upper_idx = np.where(x_make == 10.0)[0][0] | ||
|
|
||
| squeeze_polynomial = Polynomial(squeeze_coeffs) | ||
| x_squeezed = x_make + squeeze_polynomial(x_make) | ||
|
|
||
| x_morph = x_make.copy() | ||
| y_morph = np.sin(x_squeezed) | ||
|
|
||
| morph = MorphSqueeze() | ||
| morph.squeeze = squeeze_coeffs | ||
|
|
||
| x_actual, y_actual, x_expected, y_expected = morph( | ||
|
||
| x_morph, y_morph, x_target, y_target | ||
| ) | ||
| y_actual = y_actual[lower_idx : upper_idx + 1] | ||
| assert np.allclose(y_actual, y_expected) | ||
|
||
|
|
||
| # Plotting code used for figures in PR comments | ||
| # https://github.com/diffpy/diffpy.morph/pull/180 | ||
| # plt.figure() | ||
| # plt.scatter(x_expected, y_expected, color='black', label='Expected') | ||
| # plt.plot(x_morph, y_morph, color='purple', label='morph') | ||
| # plt.plot(x_actual, y_actual, '--', color='gold', label='Actual') | ||
| # plt.legend() | ||
| # plt.show() | ||
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 not quite what I am after. I have a feeling that if a morph parameter is zero the morph is not refined (though I am not sure). Otherwise there would be no need in, e.g., MorphStretch to avoid instantiating the morph object for a zero stretchy, you could just pass it to the morph with a value zero and it would return an unstretched morph.
This is bad coding tbh (wrist slap to my former students), it is what we call "magic" which is always to be avoided. Kind of "hidden behavior" if you like, which is why is like magic. We need to check if this is the case, but if it is the case, then we need to handle it here. So the comment would be something like
# Zeros and non-zeros, zero coefficients will not be refinedor sthg like that. This will be hard in our case tbh and may break the existing structure of the code in which case we may need to scale back. argh, it is hard to write good code.....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.
Sorry, I didn’t consider the implications for refinement. I think the cleanest and most consistent behavior is to only skip the morphing if
squeeze is None. If the coefficients are all zero (e.g., [0, 0, 0]), the code should still run. This way, those coefficients remain part of the refinement process and aren’t treated as a special case.This should avoid any hidden or "magic" behavior and keep everything explicit. So the comment should be like
# Zeros and non-zeros, the full polynomial is applied...Let me know what you think, I can change the code accordingly.
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 don't think any user will ever want to fix one of the coefficients but tefine the others? Remember, the test captures desired behavior, not behavior that is ready to code.... We can decide not to implement things that are to hard, but that decision cubes later when we figure out it is desired but to hard
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 think someone might want to fix some coefficients and refine others. I need to think more on how to implement it. So far what I thought we could do is instead of having a squeeze parameter that is an array of coefficients, we could have different input squeeze parameters, squeeze_0, squeeze_1, etc, for each coefficient. Then we could either give them an initial value if we want to refine that coefficient or set it up as None and won't be refined. This is similar as just having different morphs for each coefficient, like shift, stretch, and add more.