@@ -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
175177def 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+ "\n This may not be an issue, but please check for your "
244+ "particular case. "
245+ "\n To 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