-
Notifications
You must be signed in to change notification settings - Fork 19
feat: add --check-increase option
#259
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: main
Are you sure you want to change the base?
Changes from 4 commits
d6b320d
0bc478a
add0413
c9ce954
99afb15
852e2de
bc4a7be
aa0f1d8
390439a
241b56d
b14da50
daa3749
bfc9186
4910a65
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:** | ||
|
|
||
| * Add ``--check-increase`` option for squeeze morph. | ||
|
|
||
| **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 |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| """Class MorphSqueeze -- Apply a polynomial to squeeze the morph | ||
| function.""" | ||
|
|
||
| import numpy | ||
| from numpy.polynomial import Polynomial | ||
| from scipy.interpolate import CubicSpline | ||
|
|
||
|
|
@@ -68,8 +69,83 @@ class MorphSqueeze(Morph): | |
| squeeze_cutoff_low = None | ||
| squeeze_cutoff_high = None | ||
|
|
||
| def __init__(self, config=None): | ||
| def __init__(self, config=None, check_increase=False): | ||
| super().__init__(config) | ||
| self.check_increase = check_increase | ||
|
|
||
| def _set_squeeze_info(self, x, x_sorted): | ||
| self.squeeze_info = {"monotonic": True, "overlapping_regions": None} | ||
| if list(x) != list(x_sorted): | ||
| if self.check_increase: | ||
| raise ValueError( | ||
| "Error: The polynomial applied by the squeeze morph has " | ||
| "resulted in a grid that is no longer strictly increasing" | ||
| ", likely due to a convergence issue. A strictly " | ||
| "increasing grid is required for diffpy.morph to compute " | ||
| "the morphed function through cubic spline interpolation. " | ||
| "Here are some suggested methods to resolve this:\n" | ||
| "(1) Please decrease the order of your polynomial and " | ||
| "try again.\n" | ||
| "(2) If you are using initial guesses of all 0, please " | ||
| "ensure your objective function only requires a small " | ||
| "polynomial squeeze to match your reference. (In other " | ||
| "words, there is good agreement between the two functions" | ||
| ".)\n" | ||
| "(3) If you expect a large polynomial squeeze to be needed" | ||
| ", please ensure your initial parameters for the " | ||
| "polynomial morph result in good agreement between your " | ||
| "reference and objective functions. One way to obtain " | ||
| "such parameters is to first apply a --hshift and " | ||
| "--stretch morph. Then, use the hshift parameter for a0 " | ||
| "and stretch parameter for a1." | ||
| ) | ||
| else: | ||
| overlapping_regions = self._get_overlapping_regions(x) | ||
| self.squeeze_info["monotonic"] = False | ||
| self.squeeze_info["overlapping_regions"] = overlapping_regions | ||
|
|
||
| def _sort_squeeze(self, x, y): | ||
| """Sort x,y according to the value of x.""" | ||
| xy = list(zip(x, y)) | ||
| xy_sorted = sorted(xy, key=lambda pair: pair[0]) | ||
| x_sorted, y_sorted = list(zip(*xy_sorted)) | ||
| return x_sorted, y_sorted | ||
|
|
||
| def _get_overlapping_regions(self, x): | ||
| diffx = numpy.diff(x) | ||
| monotomic_regions = [] | ||
| monotomic_signs = [numpy.sign(diffx[0])] | ||
| current_region = [x[0], x[1]] | ||
| for i in range(1, len(diffx)): | ||
| if numpy.sign(diffx[i]) == monotomic_signs[-1]: | ||
| current_region.append(x[i + 1]) | ||
| else: | ||
| monotomic_regions.append(current_region) | ||
| monotomic_signs.append(numpy.sign(diffx[i])) | ||
| current_region = [x[i + 1]] | ||
| monotomic_regions.append(current_region) | ||
| overlapping_regions_sign = -1 if x[0] < x[-1] else 1 | ||
| overlapping_regions_x = [ | ||
| monotomic_regions[i] | ||
| for i in range(len(monotomic_regions)) | ||
| if monotomic_signs[i] == overlapping_regions_sign | ||
| ] | ||
| overlapping_regions = [ | ||
| (min(region), max(region)) for region in overlapping_regions_x | ||
| ] | ||
| return overlapping_regions | ||
|
||
|
|
||
| def _handle_duplicates(self, x, y): | ||
| """Remove duplicated x and use the mean value of y corresponded | ||
| to the duplicated x.""" | ||
| unq_x, unq_inv = numpy.unique(x, return_inverse=True) | ||
| if len(unq_x) == len(x): | ||
| return x, y | ||
| else: | ||
| y_avg = numpy.zeros_like(unq_x) | ||
| for i in range(len(unq_x)): | ||
| y_avg[i] = numpy.array(y)[unq_inv == i].mean() | ||
| return unq_x, y_avg | ||
|
|
||
| def morph(self, x_morph, y_morph, x_target, y_target): | ||
| """Apply a polynomial to squeeze the morph function. | ||
|
|
@@ -82,9 +158,16 @@ def morph(self, x_morph, y_morph, x_target, y_target): | |
| coeffs = [self.squeeze[f"a{i}"] for i in range(len(self.squeeze))] | ||
| squeeze_polynomial = Polynomial(coeffs) | ||
| x_squeezed = self.x_morph_in + squeeze_polynomial(self.x_morph_in) | ||
| self.y_morph_out = CubicSpline(x_squeezed, self.y_morph_in)( | ||
| x_squeezed_sorted, y_morph_sorted = self._sort_squeeze( | ||
| x_squeezed, self.y_morph_in | ||
| ) | ||
| self._set_squeeze_info(x_squeezed, x_squeezed_sorted) | ||
| x_squeezed_sorted, y_morph_sorted = self._handle_duplicates( | ||
| x_squeezed_sorted, y_morph_sorted | ||
| ) | ||
| self.y_morph_out = CubicSpline(x_squeezed_sorted, y_morph_sorted)( | ||
| self.x_morph_in | ||
| ) | ||
| self.set_extrapolation_info(x_squeezed, self.x_morph_in) | ||
| self.set_extrapolation_info(x_squeezed_sorted, self.x_morph_in) | ||
|
|
||
| return self.xyallout | ||
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 error message is updated according to #248