File: fix_tests_PR2753.patch

package info (click to toggle)
qutip 5.2.1-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 7,868 kB
  • sloc: python: 44,578; cpp: 456; makefile: 175; sh: 16
file content (386 lines) | stat: -rw-r--r-- 16,478 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
From 17d39896b07979ecba40eecfc252f8f5e56f4a61 Mon Sep 17 00:00:00 2001
From: Paul Menczel <paul@menczel.net>
Date: Thu, 25 Sep 2025 16:36:40 +0900
Subject: [PATCH 1/6] Hopefully fix loky MPI deadlock

---
 qutip/solver/parallel.py | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/qutip/solver/parallel.py b/qutip/solver/parallel.py
index 425a317f21..dd9e84d41d 100644
--- a/qutip/solver/parallel.py
+++ b/qutip/solver/parallel.py
@@ -483,6 +483,26 @@ def mpi_pmap(task, values, task_args=None, task_kwargs=None,
 
     from mpi4py.futures import MPIPoolExecutor
 
+    # If loky is used (see loky_pmap), it starts two "resource tracker" child
+    # processes that are normally never shut down. However, using mpi4py
+    # when these child processes are still alive may lead to a deadlock.
+    # -- WE RECOMMEND NOT TO MIX loky_pmap AND mpi_pmap IN THE SAME PROGRAM --
+    # However, it happens in our test runs. For this reason, we here try to
+    # shut down the resource tracker processes manually
+    try:
+        import loky
+        loky_resource_tracker = loky.backend.resource_tracker._resource_tracker
+        if loky_resource_tracker:
+            loky_resource_tracker._stop()
+
+        if hasattr(multiprocessing, 'resource_tracker'):
+            mp_resource_tracker =\
+                multiprocessing.resource_tracker._resource_tracker
+            if mp_resource_tracker:
+                mp_resource_tracker._stop()
+    except ImportError:
+        pass
+
     # If the provided num_cpus is None, we use the default value instead.
     # We thus intentionally make it impossible to call
     #   MPIPoolExecutor(max_workers=None, ...)

From 3a936e4bbab51c729fe6004887fa82895cfb1d98 Mon Sep 17 00:00:00 2001
From: Paul Menczel <paul@menczel.net>
Date: Thu, 25 Sep 2025 16:37:05 +0900
Subject: [PATCH 2/6] Correct calculation of root mean square error

---
 qutip/utilities.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/qutip/utilities.py b/qutip/utilities.py
index 52ceaa55fd..79f18a9afb 100644
--- a/qutip/utilities.py
+++ b/qutip/utilities.py
@@ -490,7 +490,7 @@ def _rmse(fun, xdata, ydata, params):
     if (yhat == ydata).all():
         return 0
     return (
-        np.sqrt(np.mean((yhat - ydata) ** 2) / len(ydata))
+        np.sqrt(np.mean(np.abs(yhat - ydata) ** 2))
         / (np.max(ydata) - np.min(ydata))
     )
 

From 9c7318453c71212067373abd9ed0f576dff2ca8e Mon Sep 17 00:00:00 2001
From: Paul Menczel <paul@menczel.net>
Date: Thu, 25 Sep 2025 16:37:43 +0900
Subject: [PATCH 3/6] Avoid calling np.random.seed in tests, update fitting
 tolerances for fixed rmse calculation

---
 qutip/tests/core/test_brtools.py     | 11 +++++-----
 qutip/tests/core/test_environment.py | 32 ++++++++++++++--------------
 qutip/tests/solver/test_results.py   |  1 +
 qutip/tests/test_mkl.py              |  4 ++--
 qutip/tests/test_utilities.py        | 23 ++++++++++++--------
 5 files changed, 38 insertions(+), 33 deletions(-)

diff --git a/qutip/tests/core/test_brtools.py b/qutip/tests/core/test_brtools.py
index 095e40644a..77822366b2 100644
--- a/qutip/tests/core/test_brtools.py
+++ b/qutip/tests/core/test_brtools.py
@@ -10,9 +10,8 @@
 )
 
 
-def _make_rand_data(shape):
-    np.random.seed(11)
-    array = np.random.rand(*shape) + 1j*np.random.rand(*shape)
+def _make_rand_data(shape, rng):
+    array = rng.random(shape) + 1j*rng.random(shape)
     return qutip.data.Dense(array)
 
 
@@ -31,9 +30,9 @@ def _make_rand_data(shape):
                          ids=['', 'transpose', 'conj', 'dag'])
 def test_matmul_var(datatype, transleft, transright):
     shape = (5, 5)
-    np.random.seed(11)
-    left = qutip.data.to(datatype, _make_rand_data(shape))
-    right = qutip.data.to(datatype, _make_rand_data(shape))
+    rng = np.random.default_rng(seed=11)
+    left = qutip.data.to(datatype, _make_rand_data(shape, rng))
+    right = qutip.data.to(datatype, _make_rand_data(shape, rng))
 
     expected = qutip.data.matmul(
         transform[transleft](left),
diff --git a/qutip/tests/core/test_environment.py b/qutip/tests/core/test_environment.py
index 70f8253fa7..22399a9a43 100644
--- a/qutip/tests/core/test_environment.py
+++ b/qutip/tests/core/test_environment.py
@@ -532,8 +532,8 @@ def test_fixed_cf_fit(self, reference, tMax, full_ansatz, tol):
 
         assert info["Nr"] == 2
         assert info["Ni"] == 2
-        assert info["rmse_real"] < 5e-3
-        assert info["rmse_imag"] < 5e-3
+        assert info["rmse_real"] < 5e-2
+        assert info["rmse_imag"] < 5e-2
         for key in ["fit_time_real", "fit_time_imag",
                     "params_real", "params_imag", "summary"]:
             assert key in info
@@ -551,7 +551,7 @@ def test_dynamic_cf_fit(self, reference, tMax, tol, full_ansatz):
         )
         tlist = np.linspace(0, tMax, 100)[1:]  # exclude t=0
         fit, info = env.approximate(
-            "cf", tlist, target_rmse=0.01, Nr_max=3, Ni_max=3,
+            "cf", tlist, target_rmse=0.1, Nr_max=3, Ni_max=3,
             full_ansatz=full_ansatz
         )
 
@@ -564,8 +564,8 @@ def test_dynamic_cf_fit(self, reference, tMax, tol, full_ansatz):
 
         assert info["Nr"] == 1
         assert info["Ni"] == 1
-        assert info["rmse_real"] <= 0.01
-        assert info["rmse_imag"] <= 0.01
+        assert info["rmse_real"] <= 0.1
+        assert info["rmse_imag"] <= 0.1
         for key in ["fit_time_real", "fit_time_imag",
                     "params_real", "params_imag", "summary"]:
             assert key in info
@@ -594,7 +594,7 @@ def test_fixed_sd_fit(self, reference, wMax, tol):
 
         assert info["N"] == 4
         assert info["Nk"] == 1
-        assert info["rmse"] < 5e-3
+        assert info["rmse"] < 5e-2
         for key in ["fit_time", "params", "summary"]:
             assert key in info
 
@@ -611,7 +611,7 @@ def test_dynamic_sd_fit(self, reference, wMax, tol, params):
         )
         wlist = np.linspace(0, wMax, 100)
         fit, info = env.approximate(
-            "sd", wlist, Nk=1, target_rmse=0.01, Nmax=5, **params
+            "sd", wlist, Nk=1, target_rmse=0.1, Nmax=5, **params
         )
 
         assert isinstance(fit, ExponentialBosonicEnvironment)
@@ -623,14 +623,14 @@ def test_dynamic_sd_fit(self, reference, wMax, tol, params):
 
         assert info["N"] < 5
         assert info["Nk"] == 1
-        assert info["rmse"] < 0.01
+        assert info["rmse"] < 0.1
         for key in ["fit_time", "params", "summary"]:
             assert key in info
 
     @pytest.mark.parametrize(["reference", "tMax", "N", "tol"], [
         pytest.param(OhmicReference(3, .75, 10, 1),
-                     15, 8, 1e-3, id="Ohmic Example"),
-        pytest.param(UDReference(1, .5, .1, 1), 2, 4, 1e-3, id='UD Example'),
+                     15, 8, 1e-2, id="Ohmic Example"),
+        pytest.param(UDReference(1, .5, .1, 1), 2, 4, 1e-2, id='UD Example'),
     ])
     @pytest.mark.parametrize("separate", [True, False])
     def test_fixed_prony_fit(self, reference, tMax, N, tol, separate):
@@ -665,8 +665,8 @@ def test_fixed_prony_fit(self, reference, tMax, N, tol, separate):
 
     @pytest.mark.parametrize(["reference", "tMax", "N", "tol"], [
         pytest.param(OhmicReference(3, .75, 10, 1),
-                     15, 8, 1e-3, id="Ohmic Example"),
-        pytest.param(UDReference(1, .5, .1, 1), 2, 4, 1e-3, id='UD Example'),
+                     15, 8, 1e-2, id="Ohmic Example"),
+        pytest.param(UDReference(1, .5, .1, 1), 2, 4, 1e-2, id='UD Example'),
     ])
     @pytest.mark.parametrize("separate", [True, False])
     def test_fixed_esprit_fit(self, reference, tMax, N, tol, separate):
@@ -701,8 +701,8 @@ def test_fixed_esprit_fit(self, reference, tMax, N, tol, separate):
 
     @pytest.mark.parametrize(["reference", "tMax", "N", "tol"], [
         pytest.param(OhmicReference(3, .75, 10, 1),
-                     15, 8, 1e-3, id="Ohmic Example"),
-        pytest.param(UDReference(1, .5, .1, 1), 2, 2, 1e-3, id='UD Example'),
+                     15, 8, 1e-2, id="Ohmic Example"),
+        pytest.param(UDReference(1, .5, .1, 1), 2, 2, 1e-2, id='UD Example'),
     ])
     @pytest.mark.parametrize("separate", [True, False])
     def test_fixed_espira1_fit(self, reference, tMax, N, tol, separate):
@@ -737,8 +737,8 @@ def test_fixed_espira1_fit(self, reference, tMax, N, tol, separate):
 
     @pytest.mark.parametrize(["reference", "tMax", "N", "tol"], [
         pytest.param(OhmicReference(3, .75, 10, 1),
-                     15, 8, 1e-3, id="Ohmic Example"),
-        pytest.param(UDReference(1, .5, .1, 1), 2, 2, 1e-3, id='UD Example'),
+                     15, 8, 1e-2, id="Ohmic Example"),
+        pytest.param(UDReference(1, .5, .1, 1), 2, 2, 1e-2, id='UD Example'),
     ])
     @pytest.mark.parametrize("separate", [True, False])
     def test_fixed_espira2_fit(self, reference, tMax, N, tol, separate):
diff --git a/qutip/tests/solver/test_results.py b/qutip/tests/solver/test_results.py
index fd3752dce1..2835857d6e 100644
--- a/qutip/tests/solver/test_results.py
+++ b/qutip/tests/solver/test_results.py
@@ -400,6 +400,7 @@ def test_repr(self, keep_runs_results):
         if keep_runs_results:
             assert "Trajectories saved." in repr
 
+    @pytest.mark.flaky(reruns=2)
     @pytest.mark.parametrize('keep_runs_results1', [True, False])
     @pytest.mark.parametrize('keep_runs_results2', [True, False])
     def test_merge_result(self, keep_runs_results1, keep_runs_results2):
diff --git a/qutip/tests/test_mkl.py b/qutip/tests/test_mkl.py
index 734f33e25e..647d3225c5 100644
--- a/qutip/tests/test_mkl.py
+++ b/qutip/tests/test_mkl.py
@@ -19,8 +19,8 @@ def test_single_rhs_vector_real(self):
                            [1, 0, 1],
                            [0, 0, 1]])
         As = scipy.sparse.csr_matrix(Adense)
-        np.random.seed(1234)
-        x = np.random.randn(3)
+        rng = np.random.default_rng(seed=1234)
+        x = rng.standard_normal(3)
         b = As * x
         x2 = mkl_spsolve(As, b, verbose=True)
         np.testing.assert_allclose(x, x2)
diff --git a/qutip/tests/test_utilities.py b/qutip/tests/test_utilities.py
index dab481ef30..8fd8755dc9 100644
--- a/qutip/tests/test_utilities.py
+++ b/qutip/tests/test_utilities.py
@@ -124,6 +124,8 @@ def test_cpu_count(monkeypatch):
 
 
 class TestFitting:
+    rng = np.random.default_rng(seed=42)
+
     def model(self, x, a, b, c):
         return np.real(a * np.exp(-(b + 1j * c) * x))
 
@@ -145,8 +147,7 @@ def generate_data(self, request):
         fparams2 = [3, 2, .5]
         y = self.model(x, *fparams1) + self.model(x, *fparams2)
         if noisy:
-            np.random.seed(42)  
-            noise = np.random.normal(0, 0.01, len(x))
+            noise = self.rng.normal(0, 0.01, len(x))
             y += noise
         return x, y, fparams1, fparams2, noisy
 
@@ -156,11 +157,15 @@ def test_fit(self, generate_data):
             self.model, num_params=3, xdata=x, ydata=y,
             lower=[-np.inf, -np.inf, 0], target_rmse=1e-8, Nmax=2
         )
+        fit_y = self.model(x, *params[0]) + self.model(x, *params[1])
+        assert rmse == pytest.approx(
+            np.sqrt(np.mean((y - fit_y)**2)) / (np.max(y) - np.min(y))
+        )
+
         if noisy:
-            assert rmse < 1e-3
-            # The atol is the std of noise times the maximum of the signal
-            assert (np.all(np.isclose(params, [fparams1, fparams2], atol=1e-2*np.max(y))) or
-                    np.all(np.isclose(params, [fparams2, fparams1], atol=1e-2*np.max(y))))
+            assert rmse < 1e-2
+            assert (np.all(np.isclose(params, [fparams1, fparams2], atol=.2)) or
+                    np.all(np.isclose(params, [fparams2, fparams1], atol=.2)))
         else:
             assert rmse < 1e-8
             assert (np.all(np.isclose(params, [fparams1, fparams2], atol=1e-3)) or
@@ -181,7 +186,7 @@ def test_espira_I(self, generate_data):
         x, y, _, _, noisy = generate_data
         rmse, params = utils.espira1(y, 4, tol=1e-16)
         if noisy:
-            assert rmse < 1e-3
+            assert rmse < 1e-2
             np.testing.assert_allclose(self.eval_prony(len(x), params), y, atol=1e-2*np.max(y))
         else:
             assert rmse < 1e-8
@@ -191,7 +196,7 @@ def test_espira_II(self, generate_data):
         x, y, _, _, noisy = generate_data
         rmse, params = utils.espira2(y, 4, tol=1e-16)
         if noisy:
-            assert rmse < 1e-3
+            assert rmse < 1e-2
             np.testing.assert_allclose(self.eval_prony(len(x), params), y, atol=1e-2*np.max(y))
         else:
             assert rmse < 1e-8
@@ -202,7 +207,7 @@ def test_prony_methods(self, generate_data, method):
         x, y, _, _, noisy = generate_data
         rmse, params = utils.prony_methods(method, y, 4)
         if noisy:
-            assert rmse < 1e-3
+            assert rmse < 1e-2
             np.testing.assert_allclose(self.eval_prony(len(x), params), y, atol=2e-2*np.max(y))
         else:
             assert rmse < 1e-8

From 4d1d03bfc7faba5b8334659f6ed92f3dd3fdb8f9 Mon Sep 17 00:00:00 2001
From: Paul Menczel <paul@menczel.net>
Date: Fri, 26 Sep 2025 11:01:17 +0900
Subject: [PATCH 4/6] Add another flaky mark

---
 qutip/tests/solver/test_results.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/qutip/tests/solver/test_results.py b/qutip/tests/solver/test_results.py
index 2835857d6e..7a33316919 100644
--- a/qutip/tests/solver/test_results.py
+++ b/qutip/tests/solver/test_results.py
@@ -283,6 +283,7 @@ def test_NmmcResult(self, include_no_jump, martingale, result_trace):
         for i, (s1, s2) in enumerate(zip(m_res.average_states, result_trace)):
             assert s1 == s2 * qutip.fock_dm(10, i)
 
+    @pytest.mark.flaky(reruns=2)
     @pytest.mark.parametrize('keep_runs_results', [True, False])
     @pytest.mark.parametrize('include_no_jump', [True, False])
     @pytest.mark.parametrize(["e_ops", "results"], [

From d201a03e12a652ce4cf67fa5384394ce4290f863 Mon Sep 17 00:00:00 2001
From: Paul Menczel <paul@menczel.net>
Date: Fri, 26 Sep 2025 13:11:06 +0900
Subject: [PATCH 5/6] Update tolerances

---
 qutip/tests/core/test_environment.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/qutip/tests/core/test_environment.py b/qutip/tests/core/test_environment.py
index 22399a9a43..2c60a3e45f 100644
--- a/qutip/tests/core/test_environment.py
+++ b/qutip/tests/core/test_environment.py
@@ -775,7 +775,7 @@ def test_fixed_espira2_fit(self, reference, tMax, N, tol, separate):
 
     @pytest.mark.parametrize(["reference", "wMax", "tol"], [
         pytest.param(OhmicReference(3, .75, 10, 1), 15, .2, id="Ohmic Example"),
-        pytest.param(UDReference(1, .5, .1, 1), 2, 1e-4, id='UD Example'),
+        pytest.param(UDReference(1, .5, .1, 1), 2, 1e-3, id='UD Example'),
     ])
     def test_fixed_aaa_fit(self, reference, wMax, tol):
         env = BosonicEnvironment.from_spectral_density(
@@ -797,7 +797,7 @@ def test_fixed_aaa_fit(self, reference, wMax, tol):
 
     @pytest.mark.parametrize(["reference", "wMax", "tol"], [
         pytest.param(OhmicReference(3, .75, 10, 1), 15, .2, id="DL Example"),
-        pytest.param(UDReference(1, .5, .1, 1), 2, 1e-4, id='UD Example'),
+        pytest.param(UDReference(1, .5, .1, 1), 2, 1e-3, id='UD Example'),
     ])
     def test_fixed_ps_fit(self, reference, wMax, tol):
         env = BosonicEnvironment.from_spectral_density(

From 2295f5a4657f46e4ca70dc42cc14323bbdbab412 Mon Sep 17 00:00:00 2001
From: Paul Menczel <paul@menczel.net>
Date: Fri, 26 Sep 2025 15:08:37 +0900
Subject: [PATCH 6/6] Update power spectrum fitting test again since it failed
 on MacOS

---
 qutip/tests/core/test_environment.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/qutip/tests/core/test_environment.py b/qutip/tests/core/test_environment.py
index 2c60a3e45f..34b3d16959 100644
--- a/qutip/tests/core/test_environment.py
+++ b/qutip/tests/core/test_environment.py
@@ -804,7 +804,7 @@ def test_fixed_ps_fit(self, reference, wMax, tol):
             reference.spectral_density, T=reference.T, tag="test"
         )
         wlist = np.linspace(-wMax, wMax, 200)
-        fit, info = env.approximate("ps", wlist,Nmax=6)
+        fit, info = env.approximate("ps", wlist, Nmax=6, maxfev=1e5)
 
         assert isinstance(fit, ExponentialBosonicEnvironment)
         assert fit.T == env.T