c4dynamics 2.3.0__tar.gz → 2.3.2__tar.gz

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 (80) hide show
  1. c4dynamics-2.3.2/CLASSIFIERS.txt +6 -0
  2. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/PKG-INFO +18 -11
  3. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/README.md +2 -2
  4. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/__init__.py +1 -1
  5. c4dynamics-2.3.2/c4dynamics/envs/mountain_car.py +477 -0
  6. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics.egg-info/PKG-INFO +18 -11
  7. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics.egg-info/SOURCES.txt +1 -0
  8. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics.egg-info/requires.txt +6 -6
  9. c4dynamics-2.3.2/pyproject.toml +39 -0
  10. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/setup.py +11 -21
  11. c4dynamics-2.3.0/c4dynamics/envs/mountain_car.py +0 -750
  12. c4dynamics-2.3.0/pyproject.toml +0 -35
  13. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/LICENSE +0 -0
  14. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/datasets/__init__.py +0 -0
  15. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/datasets/_manager.py +0 -0
  16. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/datasets/_registry.py +0 -0
  17. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/detectors/__init__.py +0 -0
  18. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/detectors/yolo3_opencv.py +0 -0
  19. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/envs/__init__.py +0 -0
  20. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/eqm/__init__.py +0 -0
  21. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/eqm/derivs.py +0 -0
  22. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/eqm/integrate.py +0 -0
  23. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/filters/__init__.py +0 -0
  24. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/filters/ekf.py +0 -0
  25. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/filters/kalman.py +0 -0
  26. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/filters/lowpass.py +0 -0
  27. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/rotmat/__init__.py +0 -0
  28. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/rotmat/animate.py +0 -0
  29. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/rotmat/rotmat.py +0 -0
  30. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/sensors/__init__.py +0 -0
  31. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/sensors/lineofsight.py +0 -0
  32. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/sensors/radar.py +0 -0
  33. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/sensors/seeker.py +0 -0
  34. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/states/__init__.py +0 -0
  35. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/states/lib/__init__.py +0 -0
  36. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/states/lib/datapoint.py +0 -0
  37. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/states/lib/pixelpoint.py +0 -0
  38. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/states/lib/rigidbody.py +0 -0
  39. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/states/state.py +0 -0
  40. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/__init__.py +0 -0
  41. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/_struct.py +0 -0
  42. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/const.py +0 -0
  43. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/cprint.py +0 -0
  44. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/gen_gif.py +0 -0
  45. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/idx2keys.py +0 -0
  46. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/images_loader.py +0 -0
  47. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/math.py +0 -0
  48. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/plottools.py +0 -0
  49. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/plottracks.py +0 -0
  50. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/printpts.py +0 -0
  51. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/slides_gen.py +0 -0
  52. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/tictoc.py +0 -0
  53. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/video_gen.py +0 -0
  54. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics/utils/vidgen.py +0 -0
  55. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics.egg-info/dependency_links.txt +0 -0
  56. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/c4dynamics.egg-info/top_level.txt +0 -0
  57. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/setup.cfg +0 -0
  58. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_animate.py +0 -0
  59. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_const.py +0 -0
  60. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_cprint.py +0 -0
  61. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_datapoint.py +0 -0
  62. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_datasets.py +0 -0
  63. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_derivs.py +0 -0
  64. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_ekf.py +0 -0
  65. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_gen_gif.py +0 -0
  66. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_integrate.py +0 -0
  67. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_kalman.py +0 -0
  68. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_lowpass.py +0 -0
  69. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_math.py +0 -0
  70. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_pixelpoint.py +0 -0
  71. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_plotdefaults.py +0 -0
  72. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_radar.py +0 -0
  73. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_rigidbody.py +0 -0
  74. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_rotmat.py +0 -0
  75. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_seeker.py +0 -0
  76. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_state.py +0 -0
  77. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_state_assignment.py +0 -0
  78. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_state_assignment_strict.py +0 -0
  79. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_tictoc.py +0 -0
  80. {c4dynamics-2.3.0 → c4dynamics-2.3.2}/tests/test_yolov3_opencv.py +0 -0
@@ -0,0 +1,6 @@
1
+ Development Status :: 5 - Production/Stable
2
+ Intended Audience :: Developers
3
+ Programming Language :: Python :: 3
4
+ Operating System :: Unix
5
+ Operating System :: MacOS :: MacOS X
6
+ Operating System :: Microsoft :: Windows
@@ -1,22 +1,29 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: c4dynamics
3
- Version: 2.3.0
3
+ Version: 2.3.2
4
4
  Summary: Python framework for state-space modeling and algorithm development
5
5
  Author: c4dynamics
6
6
  Author-email: Ziv Meri <zivmeri@gmail.com>
7
- License: MIT
8
- Requires-Python: >=3.8,<=3.12
7
+ License-Expression: MIT
8
+ Keywords: python,state-space,dynamics,physics,algorithms,computer vision,navigation,guidance,slam,vslam,image processing,signal processing,control
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: Unix
13
+ Classifier: Operating System :: MacOS :: MacOS X
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Requires-Python: >=3.8,<3.13
9
16
  Description-Content-Type: text/markdown
10
17
  License-File: LICENSE
11
18
  Requires-Dist: imageio>=2.37.0
12
- Requires-Dist: matplotlib>=3.10.3
13
- Requires-Dist: natsort>=8.4.0
14
- Requires-Dist: numpy>=2.3.1
19
+ Requires-Dist: matplotlib>=3.9.2
20
+ Requires-Dist: natsort>=8.3.1
21
+ Requires-Dist: numpy>=1.26.0
15
22
  Requires-Dist: opencv-python>=4.11.0.86
16
- Requires-Dist: pooch>=1.8.2
17
- Requires-Dist: scipy>=1.16.0
23
+ Requires-Dist: pooch>=1.8.0
24
+ Requires-Dist: scipy>=1.13.0
18
25
  Provides-Extra: dev
19
- Requires-Dist: nbsphinx>=0.9.7; extra == "dev"
26
+ Requires-Dist: nbsphinx>=0.9.3; extra == "dev"
20
27
  Requires-Dist: Sphinx>=8.1.3; extra == "dev"
21
28
  Requires-Dist: sphinx-book-theme>=1.1.4; extra == "dev"
22
29
  Requires-Dist: sphinx_design>=0.6.1; extra == "dev"
@@ -52,7 +59,7 @@ Tsipor (bird) Dynamics (c4dynamics) is the Python framework for state-space mode
52
59
 
53
60
 
54
61
 
55
- [Documentation](https://c4dynamics.github.io/C4dynamics/)
62
+ [Documentation](https://c4dynamics.github.io/c4dynamics/)
56
63
 
57
64
 
58
65
  ## Why c4dynamics?
@@ -92,7 +99,7 @@ while maintaining flexibility and scalability.
92
99
 
93
100
 
94
101
  ## Requirements
95
- - 3.8 <= Python <= 3.12
102
+ - 3.8 <= Python < 3.13
96
103
  - Required packages are listed in [requirements.txt](requirements.txt)
97
104
 
98
105
 
@@ -26,7 +26,7 @@ Tsipor (bird) Dynamics (c4dynamics) is the Python framework for state-space mode
26
26
 
27
27
 
28
28
 
29
- [Documentation](https://c4dynamics.github.io/C4dynamics/)
29
+ [Documentation](https://c4dynamics.github.io/c4dynamics/)
30
30
 
31
31
 
32
32
  ## Why c4dynamics?
@@ -66,7 +66,7 @@ while maintaining flexibility and scalability.
66
66
 
67
67
 
68
68
  ## Requirements
69
- - 3.8 <= Python <= 3.12
69
+ - 3.8 <= Python < 3.13
70
70
  - Required packages are listed in [requirements.txt](requirements.txt)
71
71
 
72
72
 
@@ -83,7 +83,7 @@ from . import envs
83
83
  #
84
84
  # version
85
85
  ##
86
- __version__ = '2.3.0' # update also in pyproject.toml
86
+ __version__ = '2.3.2' # update also in pyproject.toml & setup.py
87
87
 
88
88
 
89
89
  #
@@ -0,0 +1,477 @@
1
+ import numpy as np
2
+ import sys
3
+ sys.path.append('.')
4
+ import c4dynamics as c4d
5
+ # import warnings
6
+ # from typing import Optional
7
+
8
+
9
+ class mountain_car(c4d.state):
10
+ '''
11
+ A mountain car environment for a reinforcement learning problem.
12
+
13
+
14
+ Parameters
15
+ ==========
16
+
17
+ mass : float
18
+ ...
19
+ gravity : float
20
+ ...
21
+ action : float
22
+ ...
23
+ dt : float
24
+ ...
25
+ velocity_lim : tuple
26
+ ...
27
+ \\ position lim cannot be set becuase it determines the shape of the problem in the cos()
28
+
29
+ See Also
30
+ ========
31
+ ...
32
+
33
+
34
+
35
+
36
+ **Dynamics**
37
+
38
+
39
+ The state variables of a mountain car are:
40
+
41
+ .. math::
42
+
43
+ X = [x, v]^T
44
+
45
+ Where:
46
+ - :math:`x` is the position of the car along the x-axis.
47
+ - :math:`v` is the velocity of the car along the x-axis.
48
+
49
+ The car is goverened by the following equations of motion which represent the dynamics
50
+ of climbing a steep hill in the presence of gravity and resistance forces:
51
+
52
+ .. math::
53
+ \\dot{x} = v
54
+
55
+ \\dot{v} = g \\cdot cos(3 \\cdot x) + a_c / m - k \\cdot v / m
56
+
57
+ Where:
58
+ - :math:`x` is the position coordinate in :math:`x` direction. Default units :math:`[m]`
59
+ - :math:`v` is the velocity coordinate in :math:`x` direction. Default units :math:`[m/s]`
60
+ - :math:`g` is the gravity force. Defaults :math:`9.8 [N]`
61
+ - :math:`m` is the car mass. Defaults :math:`1500 [kg]`
62
+ - :math:`a_c` is the action (force) command. Defaults :math:`-20[N]` (left), :math:`0` (do nothing), or :math:`20[N]` (right)
63
+ - :math:`k` is the resistance coefficient. Defaults :math:`0.5 [1/s]`
64
+
65
+
66
+ The position and velocity are subject to the boundaries:
67
+
68
+ .. math::
69
+
70
+ x \\in speed_lim
71
+
72
+ v \\in [vmin, vmax]
73
+
74
+ Where:
75
+ - :math:`xmin` is the minimum position coordinate in :math:`x` direction. Defaults :math:`-120 [m]`
76
+ - :math:`xmax` is the maximum position coordinate in :math:`x` direction. Defaults :math:`50 [m]`
77
+ - :math:`vmin` is the minimum velocity coordinate in :math:`x` direction. Default units :math:`-30 [m/s]`
78
+ - :math:`vmax` is the maximum velocity coordinate in :math:`x` direction. Default units :math:`30 [m/s]`
79
+
80
+ Outside a boundary the vairable is clipped (extraploated with last value).
81
+
82
+
83
+ **Reward**
84
+
85
+
86
+ \\ Goal: When the car reaches :math:`xmax`, the environment provides
87
+
88
+
89
+ **Discretization**
90
+ ...
91
+
92
+
93
+ Example
94
+ =======
95
+ ...
96
+
97
+
98
+ '''
99
+
100
+
101
+ def __init__(self, mode = 'gymnasium', n_bins = 12):
102
+ """
103
+ Args
104
+ ----
105
+ mass: mass of the car (default 0.2)
106
+ friction: friction in Newton (default 0.3)
107
+ dt: time step in seconds (default 0.1)
108
+ """
109
+
110
+ self.mode = mode
111
+ self.n_bins = n_bins
112
+
113
+ if self.mode == 'gymnasium':
114
+ self.steep_factor = 1
115
+
116
+ self.dt = 1
117
+ self.gravity = 0.0025
118
+
119
+ self.mass = 1
120
+ self.cos_factor = 1
121
+
122
+ self.friction = 0
123
+ self.position_lim = (-1.2, 0.5)
124
+
125
+ self.speed_lim = (-0.07, 0.07)
126
+ self.action_list = (-0.001, 0, 0.001)
127
+
128
+ self.normalized = True
129
+
130
+ elif self.mode == 'sarsa':
131
+ self.steep_factor = 1
132
+
133
+ self.dt = 0.1
134
+ self.gravity = 9.8
135
+
136
+ self.mass = 0.1
137
+ self.cos_factor = 1
138
+
139
+ self.friction = 0.1
140
+ self.position_lim = (-1.2, 0.5)
141
+
142
+ self.speed_lim = (-.5, .5)
143
+ self.action_list = (-1, 0, 1) #(-1 * k * 0.31, 0, 1 * k * 0.31) # .25, .3 - FAIL, .75, .5, .333, .32 - SHARP PASS, .31 - gradual (but still not seems momentum-based)
144
+
145
+ self.normalized = True
146
+
147
+ elif self.mode == 'orig_sarsa':
148
+ self.steep_factor = 1
149
+
150
+ self.dt = 0.1
151
+ self.gravity = 9.8
152
+
153
+ self.mass = 0.2
154
+ self.cos_factor = 1
155
+
156
+ self.friction = 0.3
157
+ self.position_lim = (-1.2, 0.5)
158
+
159
+ self.speed_lim = (-1.5, 1.5) # (-3, 3)
160
+ self.action_list = (-1, 0, 1) # it seems like +-0.2 but its not because he mistakenly multiplies the gravity andthe friction by the mass. i'm not sure about that anymore.
161
+
162
+ self.normalized = False
163
+
164
+ elif self.mode == 'physical': # physical scene
165
+
166
+ self.dt = 0.1
167
+ self.gravity = 9.8
168
+
169
+ self.mass = 1500 # kg
170
+ self.cos_factor = 100
171
+ self.steep_factor = 100
172
+
173
+ self.friction = 0
174
+ self.position_lim = (-120, 50) # m
175
+
176
+ self.speed_lim = (-30, 30) # m/s
177
+ self.action_list = (-10, 0, 10)
178
+
179
+ self.normalized = True
180
+
181
+
182
+ super().__init__(position = 0, velocity = 0)
183
+
184
+ xmax = self.position_lim[1]
185
+ vmax = self.speed_lim[1]
186
+ self.e_max = self.mass * self.gravity * (np.sin((xmax - (0.5 * np.pi)) + 0.5) + 1.0) + 0.5 * vmax**2
187
+
188
+ self.state_lim = (-1, 1)
189
+ self.state_interval = np.linspace(self.state_lim[0], self.state_lim[1], num = self.n_bins - 1, endpoint = False)
190
+ self.pos_interval = np.linspace(self.position_lim[0], self.position_lim[1], num = self.n_bins - 1, endpoint = False)
191
+ self.vel_interval = np.linspace(self.speed_lim[0], self.speed_lim[1], num = self.n_bins - 1, endpoint = False)
192
+
193
+
194
+ def step(self, action):
195
+ """
196
+ Performs one step in the environment following the action.
197
+
198
+ Args
199
+ ----
200
+ action: an integer representing one of three actions [0, 1, 2]
201
+ where 0=move_left, 1=do_not_move, 2=move_right
202
+
203
+ Returns
204
+ (postion_t1, velocity_t1): state
205
+ reward: always negative but when the goal is reached
206
+ done: True when the goal is reached
207
+
208
+ """
209
+
210
+
211
+ # Semi-implicit Euler integraton
212
+ acc = self.action_list[action] / self.mass \
213
+ - self.gravity * self.steep_factor * np.cos(3 * self.position / self.cos_factor) \
214
+ - self.friction * self.velocity / self.mass
215
+
216
+ self.velocity += acc * self.dt
217
+ self.velocity = np.clip(self.velocity, self.speed_lim[0], self.speed_lim[1])
218
+
219
+ self.position += self.velocity * self.dt
220
+ self.position = np.clip(self.position, self.position_lim[0], self.position_lim[1])
221
+
222
+ if self.position <= self.position_lim[0] and self.velocity < 0:
223
+ self.velocity = 0
224
+
225
+ self.store()
226
+ reward, done = (-0.01, False) if self.position < self.position_lim[1] else (1, True)
227
+
228
+ return reward, done
229
+
230
+
231
+ def energy_normalized(self, state = None):
232
+
233
+ if state is None:
234
+ state = self.X
235
+
236
+ e_state = self.mass * self.gravity * (np.sin((state[0] - (0.5 * np.pi)) + 0.5) + 1.0) + 0.5 * state[1]**2
237
+ return 2 * e_state / self.e_max
238
+
239
+
240
+ def energy(self, state = None):
241
+
242
+ if state is None:
243
+ state = self.X
244
+
245
+ # potential energy (height modeled by sine curve)
246
+ potential = self.mass * self.gravity * (np.sin((x - (0.5 * np.pi)) + 0.5) + 1.0)
247
+ # kinetic energy
248
+ kinetic = 0.5 * self.mass * v**2
249
+
250
+ e_state = self.mass * self.gravity * (np.sin((state[0] - (0.5 * np.pi)) + 0.5) + 1.0) + 0.5 * state[1]**2
251
+ return 2 * e_state / self.e_max
252
+
253
+
254
+ def reset(self, exploring_starts = True):
255
+ """
256
+ Resets the car to an initial position [-1.2, 0.5]
257
+
258
+ Args
259
+ ----
260
+ exploring_starts: if True a random position is taken
261
+ initial_position: the initial position of the car (requires exploring_starts=False)
262
+
263
+ Returns
264
+ -------
265
+ Initial position of the car and the velocity
266
+
267
+ """
268
+
269
+ # seed_seq = np.random.SeedSequence(seed)
270
+ # np_seed = seed_seq.entropy
271
+ # rng = RandomNumberGenerator(np.random.PCG64(seed_seq))
272
+ # seed = np.random.randint(0, 2**24)
273
+ # np.random.seed(seed)
274
+ super().reset()
275
+
276
+ if exploring_starts: # gym: always generates. but on small interval (-0.6,-0.4 uniformly)
277
+
278
+ # initial_position = np.random.uniform(-1.2, 0.5)
279
+ # initial_position = np.random.uniform(initial_position - 0.1, initial_position + 0.1)
280
+ pos0 = self.position_lim[0] + 0.3888 * (self.position_lim[1] - self.position_lim[0])
281
+ dpos = 0.0555 * (self.position_lim[1] - self.position_lim[0])
282
+ initial_position = np.random.uniform(pos0 - dpos, pos0 + dpos)
283
+
284
+ else:
285
+ initial_position = -0.5
286
+
287
+ initial_position = np.clip(initial_position, self.position_lim[0], self.position_lim[1])
288
+ self.position = initial_position
289
+ self.velocity = 0
290
+
291
+ self.store()
292
+
293
+
294
+ def discretize(self):
295
+
296
+ if self.normalized:
297
+ # normalized the state to (-1, 1) and discretize for N bins.
298
+ pos = np.digitize(np.interp(self.position, self.position_lim, self.state_lim), self.state_interval)
299
+ vel = np.digitize(np.interp(self.velocity, self.speed_lim, self.state_lim), self.state_interval)
300
+
301
+ else:
302
+ # normalized the state to (-1, 1) and discretize for N bins.
303
+ pos = np.digitize(self.position, self.pos_interval)
304
+ vel = np.digitize(self.velocity, self.vel_interval)
305
+
306
+ return vel, pos
307
+
308
+
309
+ def render(self, file_path = './simulation_render.gif', mode = 'gif'):
310
+ # def render(self, file_path = './mountain_car.mp4', mode = 'mp4'):
311
+ """
312
+ When the method is called it saves an animation
313
+ of what happened until that point in the episode.
314
+ Ideally it should be called at the end of the episode,
315
+ and every k episodes.
316
+
317
+ ATTENTION: requires avconv and/or imagemagick installed.
318
+
319
+ Args
320
+ ----
321
+ file_path: the name and path of the video file
322
+ mode: the file can be saved as 'gif' or 'mp4'
323
+
324
+ """
325
+
326
+ # Plot init
327
+ fig = plt.figure()
328
+ ax = fig.add_subplot(111, autoscale_on = False, xlim = (-1.2, 0.5), ylim = (-1.1, 1.1))
329
+ ax.grid(False) # disable the grid
330
+ x_sin = np.linspace(start = -1.2, stop = 0.5, num = 100)
331
+ y_sin = np.sin(3 * x_sin)
332
+
333
+ # plt.plot(x, y)
334
+ ax.plot(x_sin, y_sin) # plot the sine wave
335
+ # line, _ = ax.plot(x, y, 'o-', lw=2)
336
+ dot, = ax.plot([], [], 'ro')
337
+ time_text = ax.text(0.05, 0.9, '', transform = ax.transAxes)
338
+ _position_list = self.data('position')[1]
339
+ _dt = self.dt
340
+
341
+ def _init():
342
+ dot.set_data([], [])
343
+ time_text.set_text('')
344
+ return dot, time_text
345
+
346
+ def _animate(i):
347
+ x = _position_list[i]
348
+ y = np.sin(3 * x)
349
+ dot.set_data([x], [y])
350
+ time_text.set_text("Time: " + str(np.round(i * _dt, 1)) + "s" + '\n' + "Frame: " + str(i))
351
+ return dot, time_text
352
+
353
+
354
+ # Argument | Meaning
355
+ # -------- | -------
356
+ # fig | The figure object where the animation will be drawn.
357
+ # _animate | The function that updates the animation frame by frame.
358
+ # np.arange(1, | The sequence of frame numbers (i) that _animate(i) will receive.
359
+ # len(self.data('t'))) | Starts from 1 to len(self.data('t')) - 1.
360
+ # blit = True | Optimizes rendering by only redrawing changed parts of the frame.
361
+ # init_func = _init | Calls _init() once at the beginning to set up the animation.
362
+ # repeat = False | The animation runs only once and does not loop.
363
+ ani = animation.FuncAnimation(fig, _animate, np.arange(1, len(self.data('t'))), blit = True, init_func = _init, repeat = False)
364
+
365
+ if file_path:
366
+ if mode == 'gif':
367
+ ani.save(file_path, writer = 'imagemagick', fps = int(1 / self.dt))
368
+ elif mode == 'mp4':
369
+ ani.save(file_path, fps = int(1/self.dt), writer='avconv', codec='libx264')
370
+
371
+ plt.show()
372
+ # Clear the figure
373
+ fig.clear()
374
+ plt.close(fig)
375
+
376
+
377
+ def render_axis(self, fig, ax):
378
+
379
+ ax.grid(False) # disable the grid
380
+ dot, = ax.plot([], [], 'ro')
381
+ time_text = ax.text(0.05, 0.9, '', transform = ax.transAxes)
382
+ _position_list = self.data('position')[1]
383
+ _dt = self.dt
384
+
385
+ def _init():
386
+ dot.set_data([], [])
387
+ time_text.set_text('')
388
+ return dot, time_text
389
+
390
+ def _animate(i):
391
+ x = _position_list[i]
392
+ y = np.sin(3 * x)
393
+ dot.set_data([x], [y])
394
+ time_text.set_text("Time: " + str(np.round(i * _dt, 1)) + "s" + '\n' + "Frame: " + str(i))
395
+ return dot, time_text
396
+
397
+ ani = animation.FuncAnimation(fig, _animate
398
+ , np.arange(1, len(self.data('t')))
399
+ , blit = True, init_func = _init
400
+ , repeat = False)
401
+
402
+ return ani, dot
403
+
404
+
405
+ def animate(self, file_path = './simulation_animate.gif', debug = False):
406
+
407
+ from animation_tools import animateit
408
+ from IPython.display import Image
409
+
410
+ # debug = False
411
+ # # Get data
412
+ # t_array = self.data('t')
413
+ # x_array = self.data('position')[1]
414
+ # dt = self.dt
415
+
416
+ # inputs = [(i, x_array[i], dt) for i in range(len(t_array))]
417
+
418
+ # if debug:
419
+ # frames = [render_frame(inp) for inp in inputs]
420
+ # else:
421
+ # # Parallel rendering
422
+ # with Pool() as pool:
423
+ # # frames = pool.map(render_frame, inputs)
424
+ # results = pool.map(square, range(10))
425
+
426
+ # frames = animateit(self)
427
+
428
+ # Get data
429
+ t_array = self.data('t')
430
+ x_array = self.data('position')[1]
431
+ dt = self.dt
432
+
433
+ inputs = [(i, x_array[i], dt) for i in range(len(t_array))]
434
+
435
+ if debug:
436
+ frames = [render_frame(inp) for inp in inputs]
437
+ else:
438
+ # Parallel rendering
439
+ with Pool() as pool:
440
+ frames = pool.map(render_frame, inputs)
441
+ # frames = pool.map(square, range(10))
442
+
443
+ # gifname = 'car_rl.gif'
444
+ imageio.mimsave(file_path, frames, duration = 0.1, loop = 0)
445
+ # c4d.gif(outfol, gifname, duration = 1)
446
+ Image(file_path) # doctest: +IGNORE_OUTPUT
447
+
448
+
449
+ def _plot(self, ax, alpha = 0.4):
450
+
451
+ # Plot init
452
+ # fig = plt.figure()
453
+ # ax = fig.add_subplot(111, autoscale_on = False, xlim = (-1.2, 0.5), ylim = (-1.1, 1.1))
454
+ ax.grid(False) # disable the grid
455
+ # ax.set_facecolor((0, 1, 0, alpha))
456
+
457
+ x = np.linspace(start = self.position_lim[0], stop = self.position_lim[1], num = 100)
458
+ z = np.sin(3 * x / self.cos_factor)
459
+ ax.plot(x, z, color = '#003366') # plot the sine wave
460
+ ax.fill_between(x, ax.get_ylim()[0], z, color = '#9370DB', alpha = alpha)
461
+
462
+ # plt.plot(x, y)
463
+ # line, _ = ax.plot(x, y, 'o-', lw=2)
464
+ positions_x = self.data('position')[1]
465
+ positions_z = np.sin(3 * self.data('position')[1] / self.cos_factor)
466
+
467
+ ax.plot(positions_x, positions_z, 'b.', alpha = 0.3)
468
+ indmaxx = np.argmax(positions_x)
469
+ ax.plot(positions_x[indmaxx], positions_z[indmaxx], 'bo')
470
+
471
+ # plt.show()
472
+ # plt.savefig(file_path)
473
+
474
+
475
+
476
+
477
+
@@ -1,22 +1,29 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: c4dynamics
3
- Version: 2.3.0
3
+ Version: 2.3.2
4
4
  Summary: Python framework for state-space modeling and algorithm development
5
5
  Author: c4dynamics
6
6
  Author-email: Ziv Meri <zivmeri@gmail.com>
7
- License: MIT
8
- Requires-Python: >=3.8,<=3.12
7
+ License-Expression: MIT
8
+ Keywords: python,state-space,dynamics,physics,algorithms,computer vision,navigation,guidance,slam,vslam,image processing,signal processing,control
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: Unix
13
+ Classifier: Operating System :: MacOS :: MacOS X
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Requires-Python: >=3.8,<3.13
9
16
  Description-Content-Type: text/markdown
10
17
  License-File: LICENSE
11
18
  Requires-Dist: imageio>=2.37.0
12
- Requires-Dist: matplotlib>=3.10.3
13
- Requires-Dist: natsort>=8.4.0
14
- Requires-Dist: numpy>=2.3.1
19
+ Requires-Dist: matplotlib>=3.9.2
20
+ Requires-Dist: natsort>=8.3.1
21
+ Requires-Dist: numpy>=1.26.0
15
22
  Requires-Dist: opencv-python>=4.11.0.86
16
- Requires-Dist: pooch>=1.8.2
17
- Requires-Dist: scipy>=1.16.0
23
+ Requires-Dist: pooch>=1.8.0
24
+ Requires-Dist: scipy>=1.13.0
18
25
  Provides-Extra: dev
19
- Requires-Dist: nbsphinx>=0.9.7; extra == "dev"
26
+ Requires-Dist: nbsphinx>=0.9.3; extra == "dev"
20
27
  Requires-Dist: Sphinx>=8.1.3; extra == "dev"
21
28
  Requires-Dist: sphinx-book-theme>=1.1.4; extra == "dev"
22
29
  Requires-Dist: sphinx_design>=0.6.1; extra == "dev"
@@ -52,7 +59,7 @@ Tsipor (bird) Dynamics (c4dynamics) is the Python framework for state-space mode
52
59
 
53
60
 
54
61
 
55
- [Documentation](https://c4dynamics.github.io/C4dynamics/)
62
+ [Documentation](https://c4dynamics.github.io/c4dynamics/)
56
63
 
57
64
 
58
65
  ## Why c4dynamics?
@@ -92,7 +99,7 @@ while maintaining flexibility and scalability.
92
99
 
93
100
 
94
101
  ## Requirements
95
- - 3.8 <= Python <= 3.12
102
+ - 3.8 <= Python < 3.13
96
103
  - Required packages are listed in [requirements.txt](requirements.txt)
97
104
 
98
105
 
@@ -1,3 +1,4 @@
1
+ CLASSIFIERS.txt
1
2
  LICENSE
2
3
  README.md
3
4
  pyproject.toml
@@ -1,13 +1,13 @@
1
1
  imageio>=2.37.0
2
- matplotlib>=3.10.3
3
- natsort>=8.4.0
4
- numpy>=2.3.1
2
+ matplotlib>=3.9.2
3
+ natsort>=8.3.1
4
+ numpy>=1.26.0
5
5
  opencv-python>=4.11.0.86
6
- pooch>=1.8.2
7
- scipy>=1.16.0
6
+ pooch>=1.8.0
7
+ scipy>=1.13.0
8
8
 
9
9
  [dev]
10
- nbsphinx>=0.9.7
10
+ nbsphinx>=0.9.3
11
11
  Sphinx>=8.1.3
12
12
  sphinx-book-theme>=1.1.4
13
13
  sphinx_design>=0.6.1
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=80.9.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "c4dynamics"
7
+ version = "2.3.2"
8
+ description = "Python framework for state-space modeling and algorithm development"
9
+ authors = [{name = "Ziv Meri", email="zivmeri@gmail.com"}]
10
+ readme = "README.md"
11
+ requires-python = ">=3.8, <3.13"
12
+ license = "MIT"
13
+ license-files = ["LICENSE"]
14
+ dependencies = [
15
+ "imageio>=2.37.0",
16
+ "matplotlib>=3.9.2",
17
+ "natsort>=8.3.1",
18
+ "numpy>=1.26.0",
19
+ "opencv-python>=4.11.0.86",
20
+ "pooch>=1.8.0",
21
+ "scipy>=1.13.0"
22
+ ]
23
+ keywords = ['python', 'state-space', 'dynamics', 'physics', 'algorithms', 'computer vision', 'navigation', 'guidance', 'slam', 'vslam', 'image processing', 'signal processing', 'control']
24
+ dynamic = ["classifiers"]
25
+
26
+ [tool.setuptools.dynamic]
27
+ classifiers = { file = "CLASSIFIERS.txt" }
28
+
29
+
30
+ # Optional / development dependencies
31
+ [project.optional-dependencies]
32
+ dev = [
33
+ "nbsphinx>=0.9.3",
34
+ "Sphinx>=8.1.3",
35
+ "sphinx-book-theme>=1.1.4",
36
+ "sphinx_design>=0.6.1"
37
+ ]
38
+
39
+