open-space-toolkit-mathematics 4.6.0__py39-none-manylinux2014_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. open_space_toolkit_mathematics-4.6.0.dist-info/METADATA +28 -0
  2. open_space_toolkit_mathematics-4.6.0.dist-info/RECORD +68 -0
  3. open_space_toolkit_mathematics-4.6.0.dist-info/WHEEL +5 -0
  4. open_space_toolkit_mathematics-4.6.0.dist-info/top_level.txt +1 -0
  5. open_space_toolkit_mathematics-4.6.0.dist-info/zip-safe +1 -0
  6. ostk/__init__.py +1 -0
  7. ostk/mathematics/OpenSpaceToolkitMathematicsPy.cpython-39-aarch64-linux-gnu.so +0 -0
  8. ostk/mathematics/__init__.py +5 -0
  9. ostk/mathematics/__init__.pyi +11 -0
  10. ostk/mathematics/curve_fitting/__init__.pyi +155 -0
  11. ostk/mathematics/curve_fitting/interpolator.pyi +243 -0
  12. ostk/mathematics/geometry/__init__.pyi +504 -0
  13. ostk/mathematics/geometry/d2/__init__.pyi +809 -0
  14. ostk/mathematics/geometry/d2/object.pyi +1779 -0
  15. ostk/mathematics/geometry/d3/__init__.pyi +1032 -0
  16. ostk/mathematics/geometry/d3/object.pyi +3709 -0
  17. ostk/mathematics/geometry/d3/transformation/__init__.pyi +3 -0
  18. ostk/mathematics/geometry/d3/transformation/rotation.pyi +1358 -0
  19. ostk/mathematics/libopen-space-toolkit-mathematics.so.4 +0 -0
  20. ostk/mathematics/object.pyi +387 -0
  21. ostk/mathematics/solver.pyi +342 -0
  22. ostk/mathematics/test/__init__.py +1 -0
  23. ostk/mathematics/test/curve_fitting/__init__.py +1 -0
  24. ostk/mathematics/test/curve_fitting/interpolator/__init__.py +1 -0
  25. ostk/mathematics/test/curve_fitting/interpolator/test_barycentric_rational.py +44 -0
  26. ostk/mathematics/test/curve_fitting/interpolator/test_cubic_spline.py +55 -0
  27. ostk/mathematics/test/curve_fitting/interpolator/test_interpolator.py +71 -0
  28. ostk/mathematics/test/curve_fitting/interpolator/test_linear.py +45 -0
  29. ostk/mathematics/test/geometry/__init__.py +1 -0
  30. ostk/mathematics/test/geometry/d2/__init__.py +1 -0
  31. ostk/mathematics/test/geometry/d2/conftest.py +79 -0
  32. ostk/mathematics/test/geometry/d2/object/__init__.py +1 -0
  33. ostk/mathematics/test/geometry/d2/object/test_composite.py +93 -0
  34. ostk/mathematics/test/geometry/d2/object/test_line.py +57 -0
  35. ostk/mathematics/test/geometry/d2/object/test_linestring.py +174 -0
  36. ostk/mathematics/test/geometry/d2/object/test_multipolygon.py +94 -0
  37. ostk/mathematics/test/geometry/d2/object/test_point.py +213 -0
  38. ostk/mathematics/test/geometry/d2/object/test_point_set.py +100 -0
  39. ostk/mathematics/test/geometry/d2/object/test_polygon.py +370 -0
  40. ostk/mathematics/test/geometry/d2/object/test_segment.py +104 -0
  41. ostk/mathematics/test/geometry/d2/test_object.py +25 -0
  42. ostk/mathematics/test/geometry/d2/test_transformation.py +84 -0
  43. ostk/mathematics/test/geometry/d3/__init__.py +1 -0
  44. ostk/mathematics/test/geometry/d3/object/__init__.py +1 -0
  45. ostk/mathematics/test/geometry/d3/object/test_composite.py +262 -0
  46. ostk/mathematics/test/geometry/d3/object/test_cuboid.py +20 -0
  47. ostk/mathematics/test/geometry/d3/object/test_line.py +68 -0
  48. ostk/mathematics/test/geometry/d3/object/test_linestring.py +168 -0
  49. ostk/mathematics/test/geometry/d3/object/test_point.py +234 -0
  50. ostk/mathematics/test/geometry/d3/object/test_point_set.py +113 -0
  51. ostk/mathematics/test/geometry/d3/object/test_polygon.py +141 -0
  52. ostk/mathematics/test/geometry/d3/object/test_segment.py +120 -0
  53. ostk/mathematics/test/geometry/d3/objects/test_cuboid.py +20 -0
  54. ostk/mathematics/test/geometry/d3/test_intersection.py +3 -0
  55. ostk/mathematics/test/geometry/d3/test_object.py +3 -0
  56. ostk/mathematics/test/geometry/d3/test_transformation.py +3 -0
  57. ostk/mathematics/test/geometry/d3/transformation/__init__.py +1 -0
  58. ostk/mathematics/test/geometry/d3/transformation/rotation/__init__.py +1 -0
  59. ostk/mathematics/test/geometry/d3/transformation/rotation/test_euler_angle.py +138 -0
  60. ostk/mathematics/test/geometry/d3/transformation/rotation/test_quaternion.py +189 -0
  61. ostk/mathematics/test/geometry/d3/transformation/rotation/test_rotation_matrix.py +40 -0
  62. ostk/mathematics/test/geometry/d3/transformation/rotation/test_rotation_vector.py +47 -0
  63. ostk/mathematics/test/geometry/test_angle.py +345 -0
  64. ostk/mathematics/test/object/__init__.py +1 -0
  65. ostk/mathematics/test/object/test_interval.py +515 -0
  66. ostk/mathematics/test/object/test_vector.py +5 -0
  67. ostk/mathematics/test/solver/test_numerical_solver.py +176 -0
  68. ostk/mathematics/test/test_object.py +24 -0
@@ -0,0 +1,515 @@
1
+ # Apache License 2.0
2
+
3
+ import pytest
4
+
5
+ from ostk.core.type import Real
6
+ from ostk.core.type import String
7
+
8
+ from ostk.mathematics.object import RealInterval
9
+
10
+
11
+ class TestInterval:
12
+ def test_type(self):
13
+ enum_members = RealInterval.Type.__members__
14
+
15
+ assert list(enum_members.keys()) == [
16
+ "Undefined",
17
+ "Closed",
18
+ "Open",
19
+ "HalfOpenLeft",
20
+ "HalfOpenRight",
21
+ ]
22
+ assert list(enum_members.values()) == [
23
+ RealInterval.Type.Undefined,
24
+ RealInterval.Type.Closed,
25
+ RealInterval.Type.Open,
26
+ RealInterval.Type.HalfOpenLeft,
27
+ RealInterval.Type.HalfOpenRight,
28
+ ]
29
+
30
+ def test_default_constructor(self):
31
+ # Input types for RealInterval
32
+ interval_1 = RealInterval(-4.31, 1.0, RealInterval.Type.Open)
33
+ interval_2 = RealInterval(-2.0, -1.0, RealInterval.Type.Closed)
34
+ interval_3 = RealInterval(3.5, 4567.35566, RealInterval.Type.HalfOpenRight)
35
+ interval_4 = RealInterval(1.45, 1.45, RealInterval.Type.Closed)
36
+
37
+ assert isinstance(interval_1, RealInterval)
38
+ assert isinstance(interval_2, RealInterval)
39
+ assert isinstance(interval_3, RealInterval)
40
+ assert isinstance(interval_4, RealInterval)
41
+ assert interval_1 is not None
42
+ assert interval_2 is not None
43
+ assert interval_3 is not None
44
+ assert interval_4 is not None
45
+
46
+ with pytest.raises(TypeError):
47
+ interval = RealInterval(3.0, 1, RealInterval.Type.Closed)
48
+
49
+ interval_5 = RealInterval(Real(-4.31), Real(1.0), RealInterval.Type.Open)
50
+ interval_6 = RealInterval(Real(-2.0), Real(-1.0), RealInterval.Type.Closed)
51
+ interval_7 = RealInterval(
52
+ Real(3.5), Real(4567.35566), RealInterval.Type.HalfOpenRight
53
+ )
54
+ interval_8 = RealInterval(Real(1.45), Real(1.45), RealInterval.Type.Closed)
55
+
56
+ assert isinstance(interval_5, RealInterval)
57
+ assert isinstance(interval_6, RealInterval)
58
+ assert isinstance(interval_7, RealInterval)
59
+ assert isinstance(interval_8, RealInterval)
60
+ assert interval_5 is not None
61
+ assert interval_6 is not None
62
+ assert interval_7 is not None
63
+ assert interval_8 is not None
64
+
65
+ # Interval Bounds
66
+ a = -4.31
67
+ b = 3.0
68
+
69
+ # Types of RealInterval
70
+ interval_undefined = RealInterval(a, b, RealInterval.Type.Undefined)
71
+ interval_closed = RealInterval(a, b, RealInterval.Type.Closed)
72
+ interval_open = RealInterval(a, b, RealInterval.Type.Open)
73
+ interval_halfopenleft = RealInterval(a, b, RealInterval.Type.HalfOpenLeft)
74
+ interval_halfopenright = RealInterval(a, b, RealInterval.Type.HalfOpenRight)
75
+
76
+ assert isinstance(interval_undefined, RealInterval)
77
+ assert isinstance(interval_closed, RealInterval)
78
+ assert isinstance(interval_open, RealInterval)
79
+ assert isinstance(interval_halfopenleft, RealInterval)
80
+ assert isinstance(interval_halfopenright, RealInterval)
81
+
82
+ # Unvalid interval definition
83
+
84
+ with pytest.raises(RuntimeError):
85
+ invalid_interval_1 = RealInterval(4.8, 3.5, RealInterval.Type.Open)
86
+
87
+ with pytest.raises(RuntimeError):
88
+ invalid_interval_2 = RealInterval(4.8, 3.5, RealInterval.Type.Closed)
89
+
90
+ with pytest.raises(RuntimeError):
91
+ invalid_interval_3 = RealInterval(4.8, 3.5, RealInterval.Type.HalfOpenLeft)
92
+
93
+ with pytest.raises(RuntimeError):
94
+ invalid_interval_4 = RealInterval(4.8, 3.5, RealInterval.Type.HalfOpenRight)
95
+
96
+ def test_undefined_constructor(self):
97
+ undefined_interval = RealInterval.undefined()
98
+
99
+ assert isinstance(undefined_interval, RealInterval)
100
+ assert undefined_interval is not None
101
+ assert undefined_interval.is_defined() is False
102
+
103
+ @pytest.mark.parametrize(
104
+ "static_func",
105
+ [
106
+ (RealInterval.closed),
107
+ (RealInterval.open),
108
+ (RealInterval.half_open_left),
109
+ (RealInterval.half_open_right),
110
+ ],
111
+ )
112
+ def test_static_constructors(self, static_func):
113
+ a = -3.1
114
+ b = 45.6
115
+
116
+ closed_interval = static_func(a, b)
117
+
118
+ assert isinstance(closed_interval, RealInterval)
119
+ assert closed_interval is not None
120
+ assert closed_interval.is_defined()
121
+ assert closed_interval.get_lower_bound() == a
122
+ assert closed_interval.get_upper_bound() == b
123
+
124
+ def test_is_defined(self):
125
+ # Interval Bounds
126
+ a = -4.31
127
+ b = 3.0
128
+
129
+ # Types of RealInterval
130
+ interval_undefined = RealInterval(a, b, RealInterval.Type.Undefined)
131
+ interval_closed = RealInterval(a, b, RealInterval.Type.Closed)
132
+ interval_open = RealInterval(a, b, RealInterval.Type.Open)
133
+ interval_halfopenleft = RealInterval(a, b, RealInterval.Type.HalfOpenLeft)
134
+ interval_halfopenright = RealInterval(a, b, RealInterval.Type.HalfOpenRight)
135
+
136
+ assert interval_undefined.is_defined() is False
137
+ assert interval_closed.is_defined() is True
138
+ assert interval_open.is_defined() is True
139
+ assert interval_halfopenleft.is_defined() is True
140
+ assert interval_halfopenright.is_defined() is True
141
+ assert RealInterval.undefined().is_defined() is False
142
+
143
+ # def test_is_degenerate ():
144
+
145
+ def test_intersects(self):
146
+ # Interval Bounds
147
+ a = -4.31
148
+ b = 3.0
149
+
150
+ # Types of RealInterval
151
+ interval_undefined = RealInterval(a, b, RealInterval.Type.Undefined)
152
+ interval_closed = RealInterval(a, b, RealInterval.Type.Closed)
153
+ interval_open = RealInterval(a, b, RealInterval.Type.Open)
154
+ interval_halfopenleft = RealInterval(a, b, RealInterval.Type.HalfOpenLeft)
155
+ interval_halfopenright = RealInterval(a, b, RealInterval.Type.HalfOpenRight)
156
+
157
+ # Define Test Intervals
158
+ interval_left = RealInterval(-5.0, -4.5, RealInterval.Type.Closed)
159
+ interval_intersects_left = RealInterval(-5.0, -4.26, RealInterval.Type.Closed)
160
+ interval_right = RealInterval(4.56, 4.67, RealInterval.Type.Closed)
161
+ interval_intersects_right = RealInterval(2.78, 46.09, RealInterval.Type.Closed)
162
+ interval_between = RealInterval(-3.4, 2.45, RealInterval.Type.Closed)
163
+ interval_bigger = RealInterval(-45.0, 34.12, RealInterval.Type.Closed)
164
+
165
+ # Add test cases with contained intervals not RealInterval.Type.Closed...
166
+ # Add test cases with open intervals and half open intervals...
167
+
168
+ # Test intersects on undefined
169
+
170
+ # with pytest.raises(RuntimeError):
171
+
172
+ # interval_undefined.intersects
173
+
174
+ # Test intersects on closed
175
+
176
+ assert interval_closed.intersects(interval_left) is False
177
+ assert interval_closed.intersects(interval_intersects_left) is True
178
+ assert interval_closed.intersects(interval_right) is False
179
+ assert interval_closed.intersects(interval_intersects_right) is True
180
+ assert interval_closed.intersects(interval_between) is True
181
+ assert interval_closed.intersects(interval_bigger) is True
182
+
183
+ # Test intersects on open
184
+
185
+ assert interval_open.intersects(interval_left) is False
186
+ assert interval_open.intersects(interval_intersects_left) is True
187
+ assert interval_open.intersects(interval_right) is False
188
+ assert interval_open.intersects(interval_intersects_right) is True
189
+ assert interval_open.intersects(interval_between) is True
190
+ assert interval_open.intersects(interval_bigger) is True
191
+
192
+ # Test intersects on halfopenleft
193
+
194
+ assert interval_halfopenleft.intersects(interval_left) is False
195
+ assert interval_halfopenleft.intersects(interval_intersects_left) is True
196
+ assert interval_halfopenleft.intersects(interval_right) is False
197
+ assert interval_halfopenleft.intersects(interval_intersects_right) is True
198
+ assert interval_halfopenleft.intersects(interval_between) is True
199
+ assert interval_halfopenleft.intersects(interval_bigger) is True
200
+
201
+ # Test intersects on halfopenright
202
+
203
+ assert interval_halfopenright.intersects(interval_left) is False
204
+ assert interval_halfopenright.intersects(interval_intersects_left) is True
205
+ assert interval_halfopenright.intersects(interval_right) is False
206
+ assert interval_halfopenright.intersects(interval_intersects_right) is True
207
+ assert interval_halfopenright.intersects(interval_between) is True
208
+ assert interval_halfopenright.intersects(interval_bigger) is True
209
+
210
+ def test_contains_real(self):
211
+ # Interval Bounds
212
+ a = -4.31
213
+ b = 3.0
214
+
215
+ # Types of RealInterval
216
+ interval_undefined = RealInterval(a, b, RealInterval.Type.Undefined)
217
+ interval_closed = RealInterval(a, b, RealInterval.Type.Closed)
218
+ interval_open = RealInterval(a, b, RealInterval.Type.Open)
219
+ interval_halfopenleft = RealInterval(a, b, RealInterval.Type.HalfOpenLeft)
220
+ interval_halfopenright = RealInterval(a, b, RealInterval.Type.HalfOpenRight)
221
+
222
+ # Define Reals
223
+ real_left = -5.43
224
+ real_right = 3.1
225
+ real_leftbound = a
226
+ real_rightbound = b
227
+ real_between_1 = 2.3
228
+ real_between_2 = 2.999999999
229
+
230
+ # Test contains on undefined
231
+
232
+ with pytest.raises(RuntimeError):
233
+ interval_undefined.contains(real_left)
234
+
235
+ # Test contains on closed
236
+
237
+ assert interval_closed.contains(real_left) is False
238
+ assert interval_closed.contains(real_right) is False
239
+ assert interval_closed.contains(real_leftbound) is True
240
+ assert interval_closed.contains(real_rightbound) is True
241
+ assert interval_closed.contains(real_between_1) is True
242
+ assert interval_closed.contains(real_between_2) is True
243
+
244
+ # Test contains on open
245
+
246
+ assert interval_open.contains(real_left) is False
247
+ assert interval_open.contains(real_right) is False
248
+ assert interval_open.contains(real_leftbound) is False
249
+ assert interval_open.contains(real_rightbound) is False
250
+ assert interval_open.contains(real_between_1) is True
251
+ assert interval_open.contains(real_between_2) is True
252
+
253
+ # Test contains on halfopenleft
254
+
255
+ assert interval_halfopenleft.contains(real_left) is False
256
+ assert interval_halfopenleft.contains(real_right) is False
257
+ assert interval_halfopenleft.contains(real_leftbound) is False
258
+ assert interval_halfopenleft.contains(real_rightbound) is True
259
+ assert interval_halfopenleft.contains(real_between_1) is True
260
+ assert interval_halfopenleft.contains(real_between_2) is True
261
+
262
+ # Test contains on halfopenright
263
+
264
+ assert interval_halfopenright.contains(real_left) is False
265
+ assert interval_halfopenright.contains(real_right) is False
266
+ assert interval_halfopenright.contains(real_leftbound) is True
267
+ assert interval_halfopenright.contains(real_rightbound) is False
268
+ assert interval_halfopenright.contains(real_between_1) is True
269
+ assert interval_halfopenright.contains(real_between_2) is True
270
+
271
+ def test_contains_interval(self):
272
+ # Interval Bounds
273
+ a = -4.31
274
+ b = 3.0
275
+
276
+ # Types of RealInterval
277
+ interval_undefined = RealInterval(a, b, RealInterval.Type.Undefined)
278
+ interval_closed = RealInterval(a, b, RealInterval.Type.Closed)
279
+ interval_open = RealInterval(a, b, RealInterval.Type.Open)
280
+ interval_halfopenleft = RealInterval(a, b, RealInterval.Type.HalfOpenLeft)
281
+ interval_halfopenright = RealInterval(a, b, RealInterval.Type.HalfOpenRight)
282
+
283
+ # Define Test Intervals
284
+ interval_left = RealInterval(-5.0, -4.5, RealInterval.Type.Closed)
285
+ interval_intersects_left = RealInterval(-5.0, -4.56, RealInterval.Type.Closed)
286
+ interval_right = RealInterval(4.56, 4.67, RealInterval.Type.Closed)
287
+ interval_intersects_right = RealInterval(2.78, 46.09, RealInterval.Type.Closed)
288
+ interval_between = RealInterval(-3.4, 2.45, RealInterval.Type.Closed)
289
+ interval_bigger = RealInterval(-45.0, 34.12, RealInterval.Type.Closed)
290
+
291
+ # Add test cases with contained intervals not RealInterval.Type.Closed...
292
+
293
+ # Test contains on undefined
294
+
295
+ with pytest.raises(RuntimeError):
296
+ interval_undefined.contains(interval_left)
297
+
298
+ # Test contains on closed
299
+
300
+ assert interval_closed.contains(interval_left) is False
301
+ assert interval_closed.contains(interval_intersects_left) is False
302
+ assert interval_closed.contains(interval_right) is False
303
+ assert interval_closed.contains(interval_intersects_right) is False
304
+ assert interval_closed.contains(interval_between) is True
305
+ assert interval_closed.contains(interval_bigger) is False
306
+
307
+ # Test contains on open
308
+
309
+ assert interval_open.contains(interval_left) is False
310
+ assert interval_open.contains(interval_intersects_left) is False
311
+ assert interval_open.contains(interval_right) is False
312
+ assert interval_open.contains(interval_intersects_right) is False
313
+ assert interval_open.contains(interval_between) is True
314
+ assert interval_open.contains(interval_bigger) is False
315
+
316
+ # Test contains on halfopenleft
317
+
318
+ assert interval_halfopenleft.contains(interval_left) is False
319
+ assert interval_halfopenleft.contains(interval_intersects_left) is False
320
+ assert interval_halfopenleft.contains(interval_right) is False
321
+ assert interval_halfopenleft.contains(interval_intersects_right) is False
322
+ assert interval_halfopenleft.contains(interval_between) is True
323
+ assert interval_halfopenleft.contains(interval_bigger) is False
324
+
325
+ # Test contains on halfopenright
326
+
327
+ assert interval_halfopenright.contains(interval_left) is False
328
+ assert interval_halfopenright.contains(interval_intersects_left) is False
329
+ assert interval_halfopenright.contains(interval_right) is False
330
+ assert interval_halfopenright.contains(interval_intersects_right) is False
331
+ assert interval_halfopenright.contains(interval_between) is True
332
+ assert interval_halfopenright.contains(interval_bigger) is False
333
+
334
+ def test_get_bounds(self):
335
+ # Interval Bounds
336
+ a = -4.31
337
+ b = 3.0
338
+
339
+ # Types of RealInterval
340
+ interval_undefined = RealInterval(a, b, RealInterval.Type.Undefined)
341
+ interval_closed = RealInterval(a, b, RealInterval.Type.Closed)
342
+ interval_open = RealInterval(a, b, RealInterval.Type.Open)
343
+ interval_halfopenleft = RealInterval(a, b, RealInterval.Type.HalfOpenLeft)
344
+ interval_halfopenright = RealInterval(a, b, RealInterval.Type.HalfOpenRight)
345
+
346
+ # get_lower_bound
347
+ with pytest.raises(RuntimeError):
348
+ interval_undefined.get_lower_bound()
349
+
350
+ assert interval_closed.get_lower_bound() == a
351
+ assert interval_open.get_lower_bound() == a
352
+ assert interval_halfopenleft.get_lower_bound() == a
353
+ assert interval_halfopenright.get_lower_bound() == a
354
+
355
+ # get_upper_bound
356
+ with pytest.raises(RuntimeError):
357
+ interval_undefined.get_upper_bound()
358
+
359
+ assert interval_closed.get_upper_bound() == b
360
+ assert interval_open.get_upper_bound() == b
361
+ assert interval_halfopenleft.get_upper_bound() == b
362
+ assert interval_halfopenright.get_upper_bound() == b
363
+
364
+ def test_to_string(self):
365
+ # Interval Bounds
366
+ a = -4.31
367
+ b = 3.0
368
+
369
+ # Types of RealInterval
370
+ interval_undefined = RealInterval(a, b, RealInterval.Type.Undefined)
371
+ interval_closed = RealInterval(a, b, RealInterval.Type.Closed)
372
+ interval_open = RealInterval(a, b, RealInterval.Type.Open)
373
+ interval_halfopenleft = RealInterval(a, b, RealInterval.Type.HalfOpenLeft)
374
+ interval_halfopenright = RealInterval(a, b, RealInterval.Type.HalfOpenRight)
375
+
376
+ with pytest.raises(RuntimeError):
377
+ interval_undefined.to_string()
378
+
379
+ assert isinstance(interval_closed.to_string(), String)
380
+ assert interval_closed.to_string() == "[-4.3099999999999996, 3.0]"
381
+ assert interval_open.to_string() == "]-4.3099999999999996, 3.0["
382
+ assert interval_halfopenleft.to_string() == "]-4.3099999999999996, 3.0]"
383
+ assert interval_halfopenright.to_string() == "[-4.3099999999999996, 3.0["
384
+
385
+ def test_get_intersection_with(self):
386
+ test_cases = [
387
+ (
388
+ RealInterval(0.0, 10.0, RealInterval.Type.HalfOpenLeft),
389
+ RealInterval(5.0, 7.0, RealInterval.Type.HalfOpenLeft),
390
+ RealInterval(5.0, 7.0, RealInterval.Type.HalfOpenLeft),
391
+ ),
392
+ (
393
+ RealInterval(0.0, 10.0, RealInterval.Type.HalfOpenRight),
394
+ RealInterval(-15.0, 25.0, RealInterval.Type.HalfOpenRight),
395
+ RealInterval(0.0, 10.0, RealInterval.Type.HalfOpenRight),
396
+ ),
397
+ (
398
+ RealInterval(0.0, 10.0, RealInterval.Type.Open),
399
+ RealInterval(-5.0, 7.0, RealInterval.Type.Open),
400
+ RealInterval(0.0, 7.0, RealInterval.Type.Open),
401
+ ),
402
+ (
403
+ RealInterval(0.0, 10.0, RealInterval.Type.Closed),
404
+ RealInterval(5.0, 15.0, RealInterval.Type.Closed),
405
+ RealInterval(5.0, 10.0, RealInterval.Type.Closed),
406
+ ),
407
+ ]
408
+
409
+ for test_case in test_cases:
410
+ first_interval = test_case[0]
411
+ second_interval = test_case[1]
412
+ expected_interval = test_case[2]
413
+
414
+ assert (
415
+ first_interval.get_intersection_with(second_interval) == expected_interval
416
+ )
417
+
418
+ def test_get_union_with(self):
419
+ test_cases = [
420
+ (
421
+ RealInterval(0.0, 10.0, RealInterval.Type.HalfOpenLeft),
422
+ RealInterval(5.0, 7.0, RealInterval.Type.HalfOpenLeft),
423
+ RealInterval(0.0, 10.0, RealInterval.Type.HalfOpenLeft),
424
+ ),
425
+ (
426
+ RealInterval(0.0, 10.0, RealInterval.Type.HalfOpenRight),
427
+ RealInterval(-15.0, 25.0, RealInterval.Type.HalfOpenRight),
428
+ RealInterval(-15.0, 25.0, RealInterval.Type.HalfOpenRight),
429
+ ),
430
+ (
431
+ RealInterval(0.0, 10.0, RealInterval.Type.Open),
432
+ RealInterval(-5.0, 7.0, RealInterval.Type.Open),
433
+ RealInterval(-5.0, 10.0, RealInterval.Type.Open),
434
+ ),
435
+ (
436
+ RealInterval(0.0, 10.0, RealInterval.Type.Closed),
437
+ RealInterval(5.0, 15.0, RealInterval.Type.Closed),
438
+ RealInterval(0.0, 15.0, RealInterval.Type.Closed),
439
+ ),
440
+ ]
441
+
442
+ for test_case in test_cases:
443
+ first_interval = test_case[0]
444
+ second_interval = test_case[1]
445
+ expected_interval = test_case[2]
446
+
447
+ assert first_interval.get_union_with(second_interval) == expected_interval
448
+
449
+ def test_clip(self):
450
+ intervals: list[RealInterval] = [
451
+ RealInterval(0.0, 10.0, RealInterval.Type.HalfOpenLeft),
452
+ RealInterval(6.5, 8.0, RealInterval.Type.HalfOpenLeft),
453
+ ]
454
+ clipping_interval: RealInterval = RealInterval.closed(6.0, 7.0)
455
+
456
+ assert RealInterval.clip(intervals, clipping_interval) is not None
457
+
458
+ def test_sort(self):
459
+ intervals: list[RealInterval] = [
460
+ RealInterval(0.0, 10.0, RealInterval.Type.HalfOpenLeft),
461
+ RealInterval(10.0, 20.0, RealInterval.Type.HalfOpenLeft),
462
+ ]
463
+ assert RealInterval.sort(intervals) is not None
464
+ assert RealInterval.sort(intervals, False) is not None
465
+ assert RealInterval.sort(intervals, False, False) is not None
466
+
467
+ def test_merge(self):
468
+ intervals: list[RealInterval] = [
469
+ RealInterval(0.0, 10.0, RealInterval.Type.HalfOpenLeft),
470
+ RealInterval(5.0, 7.0, RealInterval.Type.HalfOpenLeft),
471
+ RealInterval(0.0, 10.0, RealInterval.Type.HalfOpenLeft),
472
+ ]
473
+
474
+ assert RealInterval.merge(intervals) is not None
475
+
476
+ def test_get_gaps(self):
477
+ intervals: list[RealInterval] = [
478
+ RealInterval(0.0, 10.0, RealInterval.Type.HalfOpenLeft),
479
+ RealInterval(5.0, 7.0, RealInterval.Type.HalfOpenLeft),
480
+ RealInterval(5.0, 15.0, RealInterval.Type.HalfOpenLeft),
481
+ ]
482
+
483
+ assert RealInterval.get_gaps(intervals) is not None
484
+
485
+ assert (
486
+ RealInterval.get_gaps(intervals, RealInterval.closed(-5.0, 25.0)) is not None
487
+ )
488
+
489
+ def test_logical_or(self):
490
+ intervals_1: list[RealInterval] = [
491
+ RealInterval(8.0, 9.0, RealInterval.Type.Closed),
492
+ RealInterval(0.0, 1.0, RealInterval.Type.HalfOpenLeft),
493
+ RealInterval(2.0, 3.0, RealInterval.Type.Open),
494
+ ]
495
+
496
+ intervals_2: list[RealInterval] = [
497
+ RealInterval(0.5, 3.5, RealInterval.Type.Open),
498
+ RealInterval(5.0, 7.0, RealInterval.Type.HalfOpenLeft),
499
+ ]
500
+
501
+ assert RealInterval.logical_or(intervals_1, intervals_2) is not None
502
+
503
+ def test_logical_and(self):
504
+ intervals_1: list[RealInterval] = [
505
+ RealInterval(8.0, 9.0, RealInterval.Type.Closed),
506
+ RealInterval(0.0, 1.0, RealInterval.Type.HalfOpenLeft),
507
+ RealInterval(2.0, 3.0, RealInterval.Type.Open),
508
+ ]
509
+
510
+ intervals_2: list[RealInterval] = [
511
+ RealInterval(0.5, 3.5, RealInterval.Type.Open),
512
+ RealInterval(5.0, 7.0, RealInterval.Type.HalfOpenLeft),
513
+ ]
514
+
515
+ assert RealInterval.logical_and(intervals_1, intervals_2) is not None
@@ -0,0 +1,5 @@
1
+ # Apache License 2.0
2
+
3
+ import pytest
4
+
5
+ import ostk.mathematics as mathematics
@@ -0,0 +1,176 @@
1
+ # Apache License 2.0
2
+
3
+ import pytest
4
+
5
+ import numpy as np
6
+ import math
7
+
8
+ from ostk.mathematics.solver import NumericalSolver
9
+
10
+
11
+ def oscillator(x, dxdt, _):
12
+ dxdt[0] = x[1]
13
+ dxdt[1] = -x[0]
14
+ return dxdt
15
+
16
+
17
+ def get_state_vec(time: float) -> np.ndarray:
18
+ return np.array([math.sin(time), math.cos(time)])
19
+
20
+
21
+ @pytest.fixture
22
+ def initial_state_vec() -> np.ndarray:
23
+ return get_state_vec(0.0)
24
+
25
+
26
+ @pytest.fixture
27
+ def numerical_solver_default_inputs() -> (
28
+ tuple[NumericalSolver.LogType, NumericalSolver.StepperType, float, float, float]
29
+ ):
30
+ log_type = NumericalSolver.LogType.NoLog
31
+ stepper_type = NumericalSolver.StepperType.RungeKuttaCashKarp54
32
+ initial_time_step = 5.0
33
+ relative_tolerance = 1.0e-12
34
+ absolute_tolerance = 1.0e-12
35
+
36
+ return (
37
+ log_type,
38
+ stepper_type,
39
+ initial_time_step,
40
+ relative_tolerance,
41
+ absolute_tolerance,
42
+ )
43
+
44
+
45
+ @pytest.fixture
46
+ def numerical_solver(numerical_solver_default_inputs) -> NumericalSolver:
47
+ return NumericalSolver(*numerical_solver_default_inputs)
48
+
49
+
50
+ @pytest.fixture
51
+ def numerical_solver_conditional() -> NumericalSolver:
52
+ return NumericalSolver(
53
+ NumericalSolver.LogType.NoLog,
54
+ NumericalSolver.StepperType.RungeKuttaDopri5,
55
+ 5.0,
56
+ 1.0e-12,
57
+ 1.0e-12,
58
+ )
59
+
60
+
61
+ class TestNumericalSolver:
62
+ def test_constructor_success(self, numerical_solver: NumericalSolver):
63
+ assert numerical_solver is not None
64
+ assert isinstance(numerical_solver, NumericalSolver)
65
+ assert numerical_solver.is_defined()
66
+
67
+ def test_comparators(self, numerical_solver: NumericalSolver):
68
+ assert numerical_solver == numerical_solver
69
+ assert (numerical_solver != numerical_solver) is False
70
+
71
+ def test_get_types(
72
+ self,
73
+ numerical_solver_default_inputs: tuple[
74
+ NumericalSolver.LogType, NumericalSolver.StepperType, float, float, float
75
+ ],
76
+ numerical_solver: NumericalSolver,
77
+ ):
78
+ (
79
+ log_type,
80
+ stepper_type,
81
+ initial_time_step,
82
+ relative_tolerance,
83
+ absolute_tolerance,
84
+ ) = numerical_solver_default_inputs
85
+
86
+ assert numerical_solver.get_log_type() == log_type
87
+ assert numerical_solver.get_stepper_type() == stepper_type
88
+ assert numerical_solver.get_time_step() == initial_time_step
89
+ assert numerical_solver.get_relative_tolerance() == relative_tolerance
90
+ assert numerical_solver.get_absolute_tolerance() == absolute_tolerance
91
+ assert numerical_solver.get_observed_state_vectors() is not None
92
+
93
+ def test_get_string_from_types(self):
94
+ assert (
95
+ NumericalSolver.string_from_stepper_type(
96
+ NumericalSolver.StepperType.RungeKutta4
97
+ )
98
+ == "RungeKutta4"
99
+ )
100
+ assert (
101
+ NumericalSolver.string_from_stepper_type(
102
+ NumericalSolver.StepperType.RungeKuttaCashKarp54
103
+ )
104
+ == "RungeKuttaCashKarp54"
105
+ )
106
+ assert (
107
+ NumericalSolver.string_from_stepper_type(
108
+ NumericalSolver.StepperType.RungeKuttaFehlberg78
109
+ )
110
+ == "RungeKuttaFehlberg78"
111
+ )
112
+ assert (
113
+ NumericalSolver.string_from_log_type(NumericalSolver.LogType.NoLog) == "NoLog"
114
+ )
115
+ assert (
116
+ NumericalSolver.string_from_log_type(NumericalSolver.LogType.LogConstant)
117
+ == "LogConstant"
118
+ )
119
+ assert (
120
+ NumericalSolver.string_from_log_type(NumericalSolver.LogType.LogAdaptive)
121
+ == "LogAdaptive"
122
+ )
123
+
124
+ def test_integrate_duration(
125
+ self, numerical_solver: NumericalSolver, initial_state_vec: np.ndarray
126
+ ):
127
+ integration_duration: float = 100.0
128
+
129
+ state_vector, _ = numerical_solver.integrate_duration(
130
+ initial_state_vec, integration_duration, oscillator
131
+ )
132
+
133
+ assert 5e-9 >= abs(state_vector[0] - math.sin(integration_duration))
134
+ assert 5e-9 >= abs(state_vector[1] - math.cos(integration_duration))
135
+
136
+ integration_durations = np.arange(100.0, 200.0, 50.0).astype(float)
137
+ solutions = numerical_solver.integrate_duration(
138
+ initial_state_vec, integration_durations, oscillator
139
+ )
140
+
141
+ for solution, integration_duration in zip(solutions, integration_durations):
142
+ state_vector, _ = solution
143
+
144
+ assert 5e-9 >= abs(state_vector[0] - math.sin(integration_duration))
145
+ assert 5e-9 >= abs(state_vector[1] - math.cos(integration_duration))
146
+
147
+ def test_integrate_time(self, numerical_solver: NumericalSolver):
148
+ start_time: float = 500.0
149
+ end_time: float = start_time + 100.0
150
+
151
+ initial_state_vec = get_state_vec(start_time)
152
+
153
+ state_vector, _ = numerical_solver.integrate_time(
154
+ initial_state_vec, start_time, end_time, oscillator
155
+ )
156
+
157
+ assert 5e-9 >= abs(state_vector[0] - math.sin(end_time))
158
+ assert 5e-9 >= abs(state_vector[1] - math.cos(end_time))
159
+
160
+ end_times = np.arange(600.0, 700.0, 50.0).astype(float)
161
+ solutions = numerical_solver.integrate_time(
162
+ initial_state_vec, start_time, end_times, oscillator
163
+ )
164
+
165
+ for solution, end_time in zip(solutions, end_times):
166
+ state_vector, _ = solution
167
+
168
+ assert 5e-9 >= abs(state_vector[0] - math.sin(end_time))
169
+ assert 5e-9 >= abs(state_vector[1] - math.cos(end_time))
170
+
171
+ def test_default(self):
172
+ assert NumericalSolver.default() is not None
173
+
174
+ def test_undefined(self):
175
+ assert NumericalSolver.undefined() is not None
176
+ assert NumericalSolver.undefined().is_defined() is False