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.
- open_space_toolkit_mathematics-4.6.0.dist-info/METADATA +28 -0
- open_space_toolkit_mathematics-4.6.0.dist-info/RECORD +68 -0
- open_space_toolkit_mathematics-4.6.0.dist-info/WHEEL +5 -0
- open_space_toolkit_mathematics-4.6.0.dist-info/top_level.txt +1 -0
- open_space_toolkit_mathematics-4.6.0.dist-info/zip-safe +1 -0
- ostk/__init__.py +1 -0
- ostk/mathematics/OpenSpaceToolkitMathematicsPy.cpython-39-aarch64-linux-gnu.so +0 -0
- ostk/mathematics/__init__.py +5 -0
- ostk/mathematics/__init__.pyi +11 -0
- ostk/mathematics/curve_fitting/__init__.pyi +155 -0
- ostk/mathematics/curve_fitting/interpolator.pyi +243 -0
- ostk/mathematics/geometry/__init__.pyi +504 -0
- ostk/mathematics/geometry/d2/__init__.pyi +809 -0
- ostk/mathematics/geometry/d2/object.pyi +1779 -0
- ostk/mathematics/geometry/d3/__init__.pyi +1032 -0
- ostk/mathematics/geometry/d3/object.pyi +3709 -0
- ostk/mathematics/geometry/d3/transformation/__init__.pyi +3 -0
- ostk/mathematics/geometry/d3/transformation/rotation.pyi +1358 -0
- ostk/mathematics/libopen-space-toolkit-mathematics.so.4 +0 -0
- ostk/mathematics/object.pyi +387 -0
- ostk/mathematics/solver.pyi +342 -0
- ostk/mathematics/test/__init__.py +1 -0
- ostk/mathematics/test/curve_fitting/__init__.py +1 -0
- ostk/mathematics/test/curve_fitting/interpolator/__init__.py +1 -0
- ostk/mathematics/test/curve_fitting/interpolator/test_barycentric_rational.py +44 -0
- ostk/mathematics/test/curve_fitting/interpolator/test_cubic_spline.py +55 -0
- ostk/mathematics/test/curve_fitting/interpolator/test_interpolator.py +71 -0
- ostk/mathematics/test/curve_fitting/interpolator/test_linear.py +45 -0
- ostk/mathematics/test/geometry/__init__.py +1 -0
- ostk/mathematics/test/geometry/d2/__init__.py +1 -0
- ostk/mathematics/test/geometry/d2/conftest.py +79 -0
- ostk/mathematics/test/geometry/d2/object/__init__.py +1 -0
- ostk/mathematics/test/geometry/d2/object/test_composite.py +93 -0
- ostk/mathematics/test/geometry/d2/object/test_line.py +57 -0
- ostk/mathematics/test/geometry/d2/object/test_linestring.py +174 -0
- ostk/mathematics/test/geometry/d2/object/test_multipolygon.py +94 -0
- ostk/mathematics/test/geometry/d2/object/test_point.py +213 -0
- ostk/mathematics/test/geometry/d2/object/test_point_set.py +100 -0
- ostk/mathematics/test/geometry/d2/object/test_polygon.py +370 -0
- ostk/mathematics/test/geometry/d2/object/test_segment.py +104 -0
- ostk/mathematics/test/geometry/d2/test_object.py +25 -0
- ostk/mathematics/test/geometry/d2/test_transformation.py +84 -0
- ostk/mathematics/test/geometry/d3/__init__.py +1 -0
- ostk/mathematics/test/geometry/d3/object/__init__.py +1 -0
- ostk/mathematics/test/geometry/d3/object/test_composite.py +262 -0
- ostk/mathematics/test/geometry/d3/object/test_cuboid.py +20 -0
- ostk/mathematics/test/geometry/d3/object/test_line.py +68 -0
- ostk/mathematics/test/geometry/d3/object/test_linestring.py +168 -0
- ostk/mathematics/test/geometry/d3/object/test_point.py +234 -0
- ostk/mathematics/test/geometry/d3/object/test_point_set.py +113 -0
- ostk/mathematics/test/geometry/d3/object/test_polygon.py +141 -0
- ostk/mathematics/test/geometry/d3/object/test_segment.py +120 -0
- ostk/mathematics/test/geometry/d3/objects/test_cuboid.py +20 -0
- ostk/mathematics/test/geometry/d3/test_intersection.py +3 -0
- ostk/mathematics/test/geometry/d3/test_object.py +3 -0
- ostk/mathematics/test/geometry/d3/test_transformation.py +3 -0
- ostk/mathematics/test/geometry/d3/transformation/__init__.py +1 -0
- ostk/mathematics/test/geometry/d3/transformation/rotation/__init__.py +1 -0
- ostk/mathematics/test/geometry/d3/transformation/rotation/test_euler_angle.py +138 -0
- ostk/mathematics/test/geometry/d3/transformation/rotation/test_quaternion.py +189 -0
- ostk/mathematics/test/geometry/d3/transformation/rotation/test_rotation_matrix.py +40 -0
- ostk/mathematics/test/geometry/d3/transformation/rotation/test_rotation_vector.py +47 -0
- ostk/mathematics/test/geometry/test_angle.py +345 -0
- ostk/mathematics/test/object/__init__.py +1 -0
- ostk/mathematics/test/object/test_interval.py +515 -0
- ostk/mathematics/test/object/test_vector.py +5 -0
- ostk/mathematics/test/solver/test_numerical_solver.py +176 -0
- 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,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
|