Skip to content

Commit b944232

Browse files
committed
Update test docstrings, warning message
1 parent 42307af commit b944232

File tree

3 files changed

+78
-36
lines changed

3 files changed

+78
-36
lines changed

src/diffpy/morph/morph_io.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,8 +451,25 @@ def handle_check_increase_warning(squeeze_morph):
451451
wmsg = (
452452
"Warning: The squeeze morph has interpolated your morphed "
453453
"function from a non-monotonically increasing grid. "
454-
"This can result in strange behavior in certain "
455-
"grid regions."
454+
"\nThis may not be an issue, but please check for your "
455+
"particular case. "
456+
"\nTo avoid squeeze making your grid non-monotonic, "
457+
"here are some suggested fixes: "
458+
"\n(1) Please decrease the order of your polynomial and "
459+
"try again. "
460+
"\n(2) If you are using initial guesses of all 0, please "
461+
"ensure your objective function only requires a small "
462+
"polynomial squeeze to match your reference. "
463+
"(In other words, there is good agreement between the two "
464+
"functions.) "
465+
"\n(3) If you expect a large polynomial squeeze to be "
466+
"needed, please ensure your initial parameters for the "
467+
"polynomial morph result in good agreement between your "
468+
"reference and objective functions. "
469+
"One way to obtain such parameters is to "
470+
"first apply a --hshift and --stretch morph. "
471+
"Then, use the hshift parameter for a0 and stretch "
472+
"parameter for a1."
456473
)
457474
warnings.warn(
458475
wmsg,

src/diffpy/morph/morphs/morphsqueeze.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def _handle_duplicates(self, x, y):
9393
if len(x_unique) == len(x):
9494
return x, y
9595
else:
96-
y_avg = numpy.zeros_like(x_unique)
96+
y_avg = numpy.zeros_like(x_unique, dtype=float)
9797
for idx, _ in enumerate(x_unique):
9898
y_avg[idx] = y[inv == idx].mean()
9999
return x_unique, y_avg

tests/test_morphsqueeze.py

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ def test_morphsqueeze(x_morph, x_target, squeeze_coeffs):
5454
x_target_expected = x_target
5555
y_target_expected = y_target
5656
# actual output
57+
# turn the coefficients into a list for passing to Polynomial
58+
# the morphsqueeze function itself requires a dictionary
5759
coeffs = [squeeze_coeffs[f"a{i}"] for i in range(len(squeeze_coeffs))]
5860
squeeze_polynomial = Polynomial(coeffs)
5961
x_squeezed = x_morph + squeeze_polynomial(x_morph)
@@ -139,16 +141,16 @@ def test_morphsqueeze_extrapolate(user_filesystem, squeeze_coeffs, wmsg_gen):
139141
coeffs = [squeeze_coeffs[f"a{i}"] for i in range(len(squeeze_coeffs))]
140142
squeeze_polynomial = Polynomial(coeffs)
141143
x_squeezed = x_morph + squeeze_polynomial(x_morph)
142-
with pytest.warns() as w:
144+
with pytest.warns() as warning:
143145
morphpy.morph_arrays(
144146
np.array([x_morph, y_morph]).T,
145147
np.array([x_target, y_target]).T,
146148
squeeze=coeffs,
147149
apply=True,
148150
)
149-
assert len(w) == 1
150-
assert w[0].category is UserWarning
151-
actual_wmsg = str(w[0].message)
151+
assert len(warning) == 1
152+
assert warning[0].category is UserWarning
153+
actual_wmsg = str(warning[0].message)
152154
expected_wmsg = wmsg_gen([min(x_squeezed), max(x_squeezed)])
153155
assert actual_wmsg == expected_wmsg
154156

@@ -173,6 +175,8 @@ def test_morphsqueeze_extrapolate(user_filesystem, squeeze_coeffs, wmsg_gen):
173175

174176

175177
def test_non_unique_grid():
178+
# Test giving morphsqueeze a non-unique grid
179+
# Expect it to return a unique grid
176180
squeeze_coeffs = {"a0": 0.01, "a1": 0.01, "a2": -0.1}
177181
x_grid = np.linspace(0, 10, 101)
178182

@@ -205,10 +209,11 @@ def test_non_unique_grid():
205209
@pytest.mark.parametrize(
206210
"squeeze_coeffs, x_morph",
207211
[
208-
# The following squeezes make the function non-monotonic
209-
({"a0": -1, "a1": -1, "a2": 2}, np.linspace(-1, 1, 101)),
212+
# The following squeezes make the function non-monotonic.
213+
# Expect code to work but issue the correct warning.
214+
([-1, -1, 2], np.linspace(-1, 1, 101)),
210215
(
211-
{"a0": -1, "a1": -1, "a2": 0, "a3": 0, "a4": 2},
216+
[-1, -1, 0, 0, 2],
212217
np.linspace(-1, 1, 101),
213218
),
214219
],
@@ -217,27 +222,40 @@ def test_squeeze_warnings(user_filesystem, squeeze_coeffs, x_morph):
217222
# call in .py
218223
x_target = x_morph
219224
y_target = np.sin(x_target)
220-
coeffs = [squeeze_coeffs[f"a{i}"] for i in range(len(squeeze_coeffs))]
221-
squeeze_polynomial = Polynomial(coeffs)
225+
squeeze_polynomial = Polynomial(squeeze_coeffs)
222226
x_squeezed = x_morph + squeeze_polynomial(x_morph)
223227
y_morph = np.sin(x_squeezed)
224228
morph = MorphSqueeze()
225229
morph.squeeze = squeeze_coeffs
226-
with pytest.warns() as w:
230+
with pytest.warns() as warning:
227231
morphpy.morph_arrays(
228232
np.array([x_morph, y_morph]).T,
229233
np.array([x_target, y_target]).T,
230-
squeeze=coeffs,
234+
squeeze=squeeze_coeffs,
231235
apply=True,
232236
)
233-
assert len(w) == 1
234-
assert w[0].category is UserWarning
235-
actual_wmsg = str(w[0].message)
237+
assert len(warning) == 1
238+
assert warning[0].category is UserWarning
239+
actual_wmsg = str(warning[0].message)
236240
expected_wmsg = (
237241
"Warning: The squeeze morph has interpolated your morphed "
238242
"function from a non-monotonically increasing grid. "
239-
"This can result in strange behavior in certain "
240-
"grid regions."
243+
"\nThis may not be an issue, but please check for your "
244+
"particular case. "
245+
"\nTo avoid squeeze making your grid non-monotonic, "
246+
"here are some suggested fixes: "
247+
"\n(1) Please decrease the order of your polynomial and try again. "
248+
"\n(2) If you are using initial guesses of all 0, please ensure "
249+
"your objective function only requires a small polynomial "
250+
"squeeze to match your reference. "
251+
"(In other words, there is good agreement between the two "
252+
"functions.) "
253+
"\n(3) If you expect a large polynomial squeeze to be needed, "
254+
"please ensure your initial parameters for the polynomial "
255+
"morph result in good agreement between your reference and "
256+
"objective functions. One way to obtain such parameters is to "
257+
"first apply a --hshift and --stretch morph. "
258+
"Then, use the hshift parameter for a0 and stretch parameter for a1."
241259
)
242260
assert expected_wmsg in actual_wmsg
243261

@@ -249,31 +267,38 @@ def test_squeeze_warnings(user_filesystem, squeeze_coeffs, x_morph):
249267
(opts, pargs) = parser.parse_args(
250268
[
251269
"--squeeze",
252-
",".join(map(str, coeffs)),
270+
",".join(map(str, squeeze_coeffs)),
253271
f"{morph_file.as_posix()}",
254272
f"{target_file.as_posix()}",
255273
"--apply",
256274
"-n",
257275
]
258276
)
259-
with pytest.warns(UserWarning) as w:
277+
with pytest.warns(UserWarning) as warning:
260278
single_morph(parser, opts, pargs, stdout_flag=False)
261-
assert len(w) == 1
262-
actual_wmsg = str(w[0].message)
279+
assert len(warning) == 1
280+
actual_wmsg = str(warning[0].message)
263281
assert expected_wmsg in actual_wmsg
264282

265283

266-
def test_handle_duplicates():
267-
x_choices = np.linspace(0, 10, 11)
268-
iter = 10
284+
@pytest.mark.parametrize(
285+
"x_sampled",
286+
[
287+
# Test one duplicate per number
288+
np.array([0, 0, 1, 1, 2, 2, 3, 3]),
289+
# Test more than one duplicates per number
290+
np.array([0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2]),
291+
# Test with only one grid number
292+
np.array([0, 0, 0, 0]),
293+
# Test no duplicates
294+
np.array([0, 1, 2, 3, 4]),
295+
],
296+
)
297+
def test_handle_duplicates(x_sampled):
269298
morph = MorphSqueeze()
270-
for i in range(iter):
271-
x_sampled = np.random.choice(x_choices, size=20)
272-
y_sampled = np.sin(x_sampled)
273-
x_handled, y_handled = morph._handle_duplicates(x_sampled, y_sampled)
274-
x_target = np.unique(x_sampled)
275-
y_target = np.array(
276-
[y_sampled[x_sampled == x].mean() for x in x_target]
277-
)
278-
assert np.allclose(x_handled, x_target)
279-
assert np.allclose(y_handled, y_target)
299+
y_sampled = np.sin(x_sampled)
300+
x_handled, y_handled = morph._handle_duplicates(x_sampled, y_sampled)
301+
x_target = np.unique(x_sampled)
302+
y_target = np.array([y_sampled[x_sampled == x].mean() for x in x_target])
303+
assert np.allclose(x_handled, x_target)
304+
assert np.allclose(y_handled, y_target)

0 commit comments

Comments
 (0)