Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
AllenDowney
GitHub Repository: AllenDowney/ModSimPy
Path: blob/master/modsim/test_modsim.py
1381 views
1
import unittest
2
from modsim import *
3
import numpy as np
4
import pandas as pd
5
6
import warnings
7
warnings.simplefilter("error", Warning)
8
9
10
class TestCartPol(unittest.TestCase):
11
def test_cart2pol(self):
12
theta, r = cart2pol(3, 4)
13
self.assertAlmostEqual(r, 5)
14
self.assertAlmostEqual(theta, 0.9272952180016122)
15
16
theta, r, z = cart2pol(2, 2, 2)
17
self.assertAlmostEqual(r, 2 * np.sqrt(2))
18
self.assertAlmostEqual(theta, np.pi / 4)
19
self.assertAlmostEqual(z, 2)
20
21
def test_pol2cart(self):
22
theta = 0.9272952180016122
23
r = 5
24
x, y = pol2cart(theta, r)
25
self.assertAlmostEqual(x, 3)
26
self.assertAlmostEqual(y, 4)
27
28
angle = np.pi/4 # 45 degrees in radians
29
r = 2 * np.sqrt(2)
30
z = 2
31
x, y, z = pol2cart(angle, r, z)
32
self.assertAlmostEqual(x, 2)
33
self.assertAlmostEqual(y, 2)
34
self.assertAlmostEqual(z, 2)
35
36
37
class TestLinspaceLinRange(unittest.TestCase):
38
def test_linspace(self):
39
warnings.simplefilter("error", Warning)
40
array = linspace(0, 1, 11)
41
self.assertEqual(len(array), 11)
42
self.assertAlmostEqual(array[0], 0)
43
self.assertAlmostEqual(array[1], 0.1)
44
self.assertAlmostEqual(array[10], 1.0)
45
46
array = linspace(0, 1, 10, endpoint=False)
47
self.assertEqual(len(array), 10)
48
self.assertAlmostEqual(array[0], 0)
49
self.assertAlmostEqual(array[1], 0.1)
50
self.assertAlmostEqual(array[9], 0.9)
51
52
def test_linrange(self):
53
array = linrange(0, 1, 0.1)
54
self.assertEqual(len(array), 11)
55
self.assertAlmostEqual(array[0], 0)
56
self.assertAlmostEqual(array[1], 0.1)
57
self.assertAlmostEqual(array[9], 0.9)
58
59
60
class TestOdeSolvers(unittest.TestCase):
61
def test_run_solve_ivp(self):
62
init = State(y=2)
63
system = System(init=init, t_0=1, t_end=3)
64
65
def slope_func(t, state, system):
66
y = state[0] if isinstance(state, np.ndarray) else state.iloc[0]
67
dydt = y + t
68
return [dydt]
69
70
results, details = run_solve_ivp(system, slope_func)
71
y_end = results.y.iloc[-1]
72
self.assertAlmostEqual(y_end, 25.5571533)
73
74
75
class TestRootFinders(unittest.TestCase):
76
def test_root_scalar(self):
77
def func(x):
78
return (x - 1) * (x - 2) * (x - 3)
79
80
res = root_scalar(func, bracket=[0, 1.9])
81
self.assertAlmostEqual(res.root, 1.0, places=5)
82
83
def test_minimize_scalar(self):
84
def func(x):
85
return (x - 2)**2 + 1
86
87
# Test with bracket
88
res = minimize_scalar(func, bracket=[0, 4])
89
self.assertAlmostEqual(res.x, 2.0, places=5)
90
self.assertAlmostEqual(res.fun, 1.0, places=5)
91
92
# Test with bounds
93
res = minimize_scalar(func, bounds=[0, 4])
94
self.assertAlmostEqual(res.x, 2.0, places=5)
95
self.assertAlmostEqual(res.fun, 1.0, places=5)
96
97
def test_maximize_scalar(self):
98
def func(x):
99
return -(x - 2)**2 + 1
100
101
# Test with bracket
102
res = maximize_scalar(func, bracket=[0, 4])
103
self.assertAlmostEqual(res.x, 2.0, places=5)
104
self.assertAlmostEqual(res.fun, 1.0, places=5)
105
106
# Test with bounds
107
res = maximize_scalar(func, bounds=[0, 4])
108
self.assertAlmostEqual(res.x, 2.0, places=5)
109
self.assertAlmostEqual(res.fun, 1.0, places=5)
110
111
112
class TestRunInterpolate(unittest.TestCase):
113
def test_has_nan(self):
114
a = [1, 2, 3]
115
self.assertFalse(has_nan(a))
116
self.assertFalse(has_nan(np.array(a)))
117
self.assertFalse(has_nan(pd.Series(a)))
118
a.append(np.nan)
119
self.assertTrue(has_nan(a))
120
self.assertTrue(has_nan(np.array(a)))
121
self.assertTrue(has_nan(pd.Series(a)))
122
123
def test_is_strictly_increasing(self):
124
a = [1, 2, 3]
125
self.assertTrue(is_strictly_increasing(a))
126
self.assertTrue(is_strictly_increasing(np.array(a)))
127
self.assertTrue(is_strictly_increasing(pd.Series(a)))
128
a.append(3)
129
self.assertFalse(is_strictly_increasing(a))
130
self.assertFalse(is_strictly_increasing(np.array(a)))
131
self.assertFalse(is_strictly_increasing(pd.Series(a)))
132
133
def test_interpolate(self):
134
index = [1, 2, 3]
135
values = np.array(index) * 2 - 1
136
series = pd.Series(values, index=index)
137
i = interpolate(series)
138
self.assertAlmostEqual(i(1.5), 2.0)
139
140
141
class TestGradient(unittest.TestCase):
142
def test_gradient(self):
143
a = [1, 2, 4]
144
s = TimeSeries(a)
145
r = gradient(s)
146
self.assertTrue(isinstance(r, pd.Series))
147
self.assertAlmostEqual(r[1], 1.5)
148
149
150
class TestVector(unittest.TestCase):
151
def assertArrayEqual(self, res, ans):
152
self.assertTrue(isinstance(res, (np.ndarray, pd.Series)))
153
self.assertTrue((res == ans).all())
154
155
def assertVectorEqual(self, res, ans):
156
self.assertTrue(isinstance(res, (pd.Series, np.ndarray)))
157
self.assertTrue((res == ans).all())
158
159
def assertVectorAlmostEqual(self, res, ans):
160
for x, y in zip(res, ans):
161
self.assertAlmostEqual(x, y)
162
163
def test_vector_mag(self):
164
v = [3, 4]
165
self.assertEqual(vector_mag(v), 5)
166
v = Vector(3, 4)
167
self.assertEqual(vector_mag(v), 5)
168
169
def test_vector_mag2(self):
170
171
v = [3, 4]
172
self.assertEqual(vector_mag2(v), 25)
173
v = Vector(3, 4)
174
self.assertEqual(vector_mag2(v), 25)
175
176
def test_vector_angle(self):
177
ans = 0.927295218
178
v = [3, 4]
179
self.assertAlmostEqual(vector_angle(v), ans)
180
v = Vector(3, 4)
181
self.assertAlmostEqual(vector_angle(v), ans)
182
183
def test_vector_hat(self):
184
v = [3, 4]
185
ans = [0.6, 0.8]
186
self.assertVectorAlmostEqual(vector_hat(v), ans)
187
188
v = Vector(3, 4)
189
self.assertVectorAlmostEqual(vector_hat(v), ans)
190
191
v = [0, 0]
192
ans = [0, 0]
193
self.assertVectorAlmostEqual(vector_hat(v), ans)
194
v = Vector(0, 0)
195
self.assertVectorAlmostEqual(vector_hat(v), ans)
196
197
def test_vector_perp(self):
198
v = [3, 4]
199
ans = [-4, 3]
200
self.assertTrue((vector_perp(v) == ans).all())
201
v = Vector(3, 4)
202
self.assertTrue((vector_perp(v) == ans).all())
203
204
def test_vector_dot(self):
205
v = [3, 4]
206
w = [5, 6]
207
ans = 39
208
self.assertAlmostEqual(vector_dot(v, w), ans)
209
v = Vector(3, 4)
210
self.assertAlmostEqual(vector_dot(v, w), ans)
211
self.assertAlmostEqual(vector_dot(w, v), ans)
212
213
def test_vector_cross_2D(self):
214
ans = -2
215
216
v = [3, 4]
217
w = [5, 6]
218
self.assertAlmostEqual(vector_cross(v, w), ans)
219
self.assertAlmostEqual(vector_cross(w, v), -ans)
220
221
v = Vector(3, 4)
222
self.assertAlmostEqual(vector_cross(v, w), ans)
223
self.assertAlmostEqual(vector_cross(w, v), -ans)
224
225
def test_vector_cross_3D(self):
226
ans = [-2, 4, -2]
227
228
v = [3, 4, 5]
229
w = [5, 6, 7]
230
res = vector_cross(v, w)
231
self.assertTrue(isinstance(res, (np.ndarray, pd.Series)))
232
self.assertTrue((res == ans).all())
233
self.assertTrue((-vector_cross(w, v) == ans).all())
234
235
v = Vector(3, 4, 5)
236
self.assertVectorEqual(vector_cross(v, w), ans)
237
self.assertVectorEqual(-vector_cross(w, v), ans)
238
239
def test_scalar_proj(self):
240
ans = 4.9934383
241
ans2 = 7.8
242
243
v = [3, 4]
244
w = [5, 6]
245
self.assertAlmostEqual(scalar_proj(v, w), ans)
246
self.assertAlmostEqual(scalar_proj(w, v), ans2)
247
248
v = Vector(3, 4)
249
self.assertAlmostEqual(scalar_proj(v, w), ans)
250
self.assertAlmostEqual(scalar_proj(w, v), ans2)
251
252
def test_vector_proj(self):
253
warnings.simplefilter("error", Warning)
254
ans = [3.19672131, 3.83606557]
255
ans2 = [4.68, 6.24]
256
257
v = [3, 4]
258
w = [5, 6]
259
self.assertVectorAlmostEqual(vector_proj(v, w), ans)
260
self.assertVectorAlmostEqual(vector_proj(w, v), ans2)
261
262
v = Vector(3, 4)
263
self.assertVectorAlmostEqual(vector_proj(v, w), ans)
264
self.assertVectorAlmostEqual(vector_proj(w, v), ans2)
265
266
def test_vector_dist(self):
267
v = [3, 4]
268
w = [6, 8]
269
ans = 5
270
self.assertAlmostEqual(vector_dist(v, w), ans)
271
self.assertAlmostEqual(vector_dist(w, v), ans)
272
273
v = Vector(3, 4)
274
self.assertAlmostEqual(vector_dist(v, w), ans)
275
self.assertAlmostEqual(vector_dist(w, v), ans)
276
277
def test_vector_diff_angle(self):
278
v = [3, 4]
279
w = [5, 6]
280
ans = 0.0512371674
281
self.assertAlmostEqual(vector_diff_angle(v, w), ans)
282
self.assertAlmostEqual(vector_diff_angle(w, v), -ans)
283
284
v = Vector(3, 4)
285
self.assertAlmostEqual(vector_diff_angle(v, w), ans)
286
self.assertAlmostEqual(vector_diff_angle(w, v), -ans)
287
288
289
class TestSeriesCopy(unittest.TestCase):
290
def test_series_copy(self):
291
series = TimeSeries()
292
res = series.copy()
293
self.assertTrue(isinstance(res, pd.Series))
294
295
296
class TestLeastsq(unittest.TestCase):
297
def test_leastsq(self):
298
# Create noise-free test data: y = 2x + 1
299
x = np.array([0, 1, 2, 3, 4])
300
y = 2 * x + 1
301
302
def error_func(params, x, y):
303
m, b = params
304
return y - (m * x + b)
305
306
# Initial guess
307
x0 = [1, 0] # m=1, b=0
308
309
# Run leastsq
310
best_params, details = leastsq(error_func, x0, x, y)
311
312
# Check results
313
self.assertAlmostEqual(best_params[0], 2.0, places=5) # slope
314
self.assertAlmostEqual(best_params[1], 1.0, places=5) # intercept
315
self.assertTrue(details.success)
316
317
318
class TestCrossings(unittest.TestCase):
319
def test_crossings(self):
320
# Create a simple linear series from 0 to 10
321
index = np.linspace(0, 10, 11)
322
values = index.copy()
323
series = pd.Series(values, index=index)
324
325
# Find where the series crosses 5
326
result = crossings(series, 5)
327
# Should cross exactly at 5
328
self.assertEqual(len(result), 1)
329
self.assertAlmostEqual(result[0], 5.0, places=5)
330
331
332
class TestDataStructures(unittest.TestCase):
333
def test_state(self):
334
s = State(a=1, b=2)
335
self.assertIsInstance(s, pd.Series)
336
self.assertEqual(s['a'], 1)
337
self.assertEqual(s['b'], 2)
338
self.assertEqual(s.name, 'state')
339
340
def test_timeseries(self):
341
ts = TimeSeries([1, 2, 3], index=[10, 20, 30])
342
self.assertIsInstance(ts, pd.Series)
343
self.assertEqual(list(ts), [1, 2, 3])
344
self.assertEqual(list(ts.index), [10, 20, 30])
345
self.assertEqual(ts.index.name, 'Time')
346
self.assertEqual(ts.name, 'Quantity')
347
348
def test_sweepseries(self):
349
ss = SweepSeries([4, 5, 6], index=[0.1, 0.2, 0.3])
350
self.assertIsInstance(ss, pd.Series)
351
self.assertEqual(list(ss), [4, 5, 6])
352
self.assertEqual(list(ss.index), [0.1, 0.2, 0.3])
353
self.assertEqual(ss.index.name, 'Parameter')
354
self.assertEqual(ss.name, 'Metric')
355
356
def test_timeframe(self):
357
df = TimeFrame([[1, 2], [3, 4]], columns=['a', 'b'], index=[0, 1])
358
self.assertIsInstance(df, pd.DataFrame)
359
self.assertEqual(df.shape, (2, 2))
360
self.assertEqual(list(df.columns), ['a', 'b'])
361
self.assertEqual(list(df.index), [0, 1])
362
363
def test_sweepframe(self):
364
df = SweepFrame([[5, 6], [7, 8]], columns=['x', 'y'], index=[0.1, 0.2])
365
self.assertIsInstance(df, pd.DataFrame)
366
self.assertEqual(df.shape, (2, 2))
367
self.assertEqual(list(df.columns), ['x', 'y'])
368
self.assertEqual(list(df.index), [0.1, 0.2])
369
370
def test_make_series(self):
371
s = make_series([10, 20, 30], [1, 2, 3])
372
self.assertIsInstance(s, pd.Series)
373
self.assertEqual(list(s.index), [10, 20, 30])
374
self.assertEqual(list(s.values), [1, 2, 3])
375
self.assertEqual(s.name, 'values')
376
self.assertEqual(s.index.name, 'index')
377
378
def test_vector_polar(self):
379
v = [3, 4]
380
mag, angle = vector_polar(v)
381
self.assertAlmostEqual(mag, 5.0, places=5)
382
self.assertAlmostEqual(angle, np.arctan2(4, 3), places=5)
383
384
def test_interpolate_inverse(self):
385
# y = 2x + 1, invert to get x from y
386
x = np.array([0, 1, 2, 3, 4])
387
y = 2 * x + 1
388
series = pd.Series(y, index=x)
389
inv = interpolate_inverse(series)
390
# y=5 -> x=2
391
self.assertAlmostEqual(inv(5), 2.0, places=5)
392
393
def test_gradient(self):
394
s = pd.Series([1, 4, 9], index=[0, 1, 2])
395
g = gradient(s)
396
self.assertIsInstance(g, pd.Series)
397
# Should be [3, 4, 5] for [1,4,9] at [0,1,2]
398
self.assertTrue(np.allclose(g, [3, 4, 5]))
399
400
def test_magnitude(self):
401
self.assertEqual(magnitude(5), 5)
402
class Dummy:
403
magnitude = 42
404
self.assertEqual(magnitude(Dummy()), 42)
405
406
def test_remove_units(self):
407
class Dummy:
408
def __init__(self):
409
self.x = 5
410
self.y = 10
411
d = Dummy()
412
res = remove_units(d)
413
self.assertEqual(res.x, 5)
414
self.assertEqual(res.y, 10)
415
416
def test_remove_units_series(self):
417
s = pd.Series({'a': 1, 'b': 2})
418
res = remove_units_series(s)
419
self.assertTrue((res == s).all())
420
421
def test_underride(self):
422
d = {'a': 1}
423
underride(d, a=2, b=3)
424
self.assertEqual(d['a'], 1)
425
self.assertEqual(d['b'], 3)
426
427
428
if __name__ == "__main__":
429
unittest.main()
430
431