AMS-BP 0.0.231__py3-none-any.whl → 0.2.0__py3-none-any.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.
AMS_BP/cells/base_cell.py DELETED
@@ -1,55 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from dataclasses import dataclass
3
- from typing import Tuple
4
- import numpy as np
5
-
6
-
7
- @dataclass
8
- class BaseCell(ABC):
9
- """
10
- Abstract base class for all cell types.
11
-
12
- Attributes:
13
- origin (np.ndarray): The (x, y) coordinates of the cell's origin in XY plane
14
- """
15
-
16
- origin: np.ndarray
17
-
18
- def __post_init__(self):
19
- """Validate inputs and convert to numpy arrays if needed."""
20
- self.origin = np.array(self.origin, dtype=float)
21
- if self.origin.shape != (2,):
22
- raise ValueError("Origin must be a 2D point (x,y)")
23
- self._validate_specific()
24
- self._volume = self._calculate_volume()
25
-
26
- @abstractmethod
27
- def _validate_specific(self) -> None:
28
- """Validate cell-specific parameters."""
29
- pass
30
-
31
- @abstractmethod
32
- def _calculate_volume(self) -> float:
33
- """Calculate the volume of the cell."""
34
- pass
35
-
36
- @property
37
- @abstractmethod
38
- def center(self) -> np.ndarray:
39
- """Get the center point of the cell."""
40
- pass
41
-
42
- @abstractmethod
43
- def get_bounds(self) -> Tuple[np.ndarray, np.ndarray]:
44
- """Get the minimum and maximum points that define the cell's bounds."""
45
- pass
46
-
47
- @abstractmethod
48
- def contains_point(self, point: np.ndarray) -> bool:
49
- """Check if a point lies within the cell."""
50
- pass
51
-
52
- @property
53
- def volume(self) -> float:
54
- """Get the pre-calculated volume of the cell."""
55
- return self._volume
@@ -1,82 +0,0 @@
1
- from dataclasses import dataclass
2
- import numpy as np
3
- from .base_cell import BaseCell
4
- from typing import Tuple
5
-
6
-
7
- @dataclass
8
- class RectangularCell(BaseCell):
9
- """
10
- Represents a rectangular cell in 3D space.
11
-
12
- Attributes:
13
- origin (np.ndarray): The (x, y) coordinates of the cell's origin in XY plane
14
- dimensions (np.ndarray): The (length, width, height) of the cell
15
- """
16
-
17
- dimensions: np.ndarray
18
-
19
- def _validate_specific(self) -> None:
20
- """Validate rectangle-specific parameters."""
21
- self.dimensions = np.array(self.dimensions, dtype=float)
22
- if self.dimensions.shape != (3,):
23
- raise ValueError("Dimensions must be a 3D vector (length, width, height)")
24
- if np.any(self.dimensions <= 0):
25
- raise ValueError("All dimensions must be positive")
26
-
27
- def _calculate_volume(self) -> float:
28
- """Calculate the volume of the rectangular cell."""
29
- return float(np.prod(self.dimensions))
30
-
31
- @property
32
- def center(self) -> np.ndarray:
33
- """Get the center point of the rectangular cell."""
34
- return np.array(
35
- [
36
- self.origin[0] + self.dimensions[0] / 2,
37
- self.origin[1] + self.dimensions[1] / 2,
38
- 0.0,
39
- ]
40
- )
41
-
42
- def get_bounds(self) -> Tuple[np.ndarray, np.ndarray]:
43
- """
44
- Get the minimum and maximum points that define the cell's bounds.
45
-
46
- Returns:
47
- Tuple containing (min_point, max_point)
48
- """
49
- min_point = np.array(
50
- [
51
- self.origin[0],
52
- self.origin[1],
53
- -self.dimensions[2] / 2, # Z extends downward from 0
54
- ]
55
- )
56
-
57
- max_point = np.array(
58
- [
59
- self.origin[0] + self.dimensions[0],
60
- self.origin[1] + self.dimensions[1],
61
- self.dimensions[2] / 2, # Z extends upward from 0
62
- ]
63
- )
64
-
65
- return min_point, max_point
66
-
67
- def contains_point(self, point: np.ndarray) -> bool:
68
- """
69
- Check if a point lies within the rectangular cell.
70
-
71
- Args:
72
- point: A 3D point to check
73
-
74
- Returns:
75
- bool: True if the point is inside the cell, False otherwise
76
- """
77
- point = np.array(point)
78
- if point.shape != (3,):
79
- raise ValueError("Point must be a 3D point")
80
-
81
- min_point, max_point = self.get_bounds()
82
- return np.all(point >= min_point) and np.all(point <= max_point)
AMS_BP/cells/rod_cell.py DELETED
@@ -1,98 +0,0 @@
1
- from dataclasses import dataclass
2
- import numpy as np
3
- from .base_cell import BaseCell
4
- from typing import Tuple
5
-
6
-
7
- @dataclass
8
- class RodCell(BaseCell):
9
- """
10
- Represents a rod-like cell in 3D space.
11
-
12
- Attributes:
13
- origin (np.ndarray): The (x, y) coordinates of the cell's origin in XY plane
14
- length (float): Total length of the rod (including end caps)
15
- radius (float): Radius of both the cylindrical body and hemispheres
16
- """
17
-
18
- length: float
19
- radius: float
20
-
21
- def _validate_specific(self) -> None:
22
- """Validate rod-specific parameters."""
23
- if self.length <= 0:
24
- raise ValueError("Length must be positive")
25
- if self.radius <= 0:
26
- raise ValueError("Radius must be positive")
27
-
28
- def _calculate_volume(self) -> float:
29
- """Calculate the volume of the rod."""
30
- cylinder_volume = np.pi * self.radius**2 * (self.length - 2 * self.radius)
31
- sphere_volume = (4 / 3) * np.pi * self.radius**3
32
- return cylinder_volume + sphere_volume
33
-
34
- @property
35
- def center(self) -> np.ndarray:
36
- """Get the center point of the rod."""
37
- return np.array([self.origin[0] + self.length / 2, self.origin[1], 0.0])
38
-
39
- def get_bounds(self) -> Tuple[np.ndarray, np.ndarray]:
40
- """
41
- Get the minimum and maximum points that define the rod's bounding box.
42
-
43
- Returns:
44
- Tuple containing (min_point, max_point)
45
- """
46
- min_point = np.array(
47
- [
48
- self.origin[0],
49
- self.origin[1] - self.radius,
50
- -self.radius, # Z extends downward from 0
51
- ]
52
- )
53
-
54
- max_point = np.array(
55
- [
56
- self.origin[0] + self.length,
57
- self.origin[1] + self.radius,
58
- self.radius, # Z extends upward from 0
59
- ]
60
- )
61
-
62
- return min_point, max_point
63
-
64
- def contains_point(self, point: np.ndarray) -> bool:
65
- """
66
- Check if a point lies within the rod.
67
-
68
- Args:
69
- point: A 3D point to check
70
-
71
- Returns:
72
- bool: True if the point is inside the rod, False otherwise
73
- """
74
- point = np.array(point)
75
- if point.shape != (3,):
76
- raise ValueError("Point must be a 3D point")
77
-
78
- # Project point onto XY plane for cylinder check
79
- x, y, z = point - np.array([self.origin[0], self.origin[1], 0])
80
-
81
- # Check if point is within the length bounds
82
- if x < 0 or x > self.length:
83
- return False
84
-
85
- # If point is within the cylindrical section
86
- if self.radius <= x <= (self.length - self.radius):
87
- # Check distance from central axis
88
- return np.sqrt(y**2 + z**2) <= self.radius
89
-
90
- # Check left hemisphere (if x < radius)
91
- elif x < self.radius:
92
- center = np.array([self.radius, 0, 0])
93
- return np.linalg.norm(np.array([x, y, z]) - center) <= self.radius
94
-
95
- # Check right hemisphere (if x > length - radius)
96
- else:
97
- center = np.array([self.length - self.radius, 0, 0])
98
- return np.linalg.norm(np.array([x, y, z]) - center) <= self.radius
@@ -1,74 +0,0 @@
1
- from dataclasses import dataclass
2
- import numpy as np
3
- from .base_cell import BaseCell
4
- from typing import Tuple
5
-
6
-
7
- @dataclass
8
- class SphericalCell(BaseCell):
9
- """
10
- Represents a spherical cell in 3D space, centered around Z=0.
11
-
12
- Attributes:
13
- origin (np.ndarray): The (x, y) coordinates of the cell's center in XY plane
14
- radius (float): Radius of the sphere
15
- """
16
-
17
- radius: float
18
-
19
- def _validate_specific(self) -> None:
20
- """Validate sphere-specific parameters."""
21
- if self.radius <= 0:
22
- raise ValueError("Radius must be positive")
23
-
24
- def _calculate_volume(self) -> float:
25
- """Calculate the volume of the sphere."""
26
- return (4 / 3) * np.pi * self.radius**3
27
-
28
- @property
29
- def center(self) -> np.ndarray:
30
- """Get the center point of the sphere."""
31
- return np.array([self.origin[0], self.origin[1], 0.0])
32
-
33
- def get_bounds(self) -> Tuple[np.ndarray, np.ndarray]:
34
- """
35
- Get the minimum and maximum points that define the sphere's bounding box.
36
-
37
- Returns:
38
- Tuple containing (min_point, max_point)
39
- """
40
- min_point = np.array(
41
- [
42
- self.origin[0] - self.radius,
43
- self.origin[1] - self.radius,
44
- -self.radius, # Z extends downward from 0
45
- ]
46
- )
47
-
48
- max_point = np.array(
49
- [
50
- self.origin[0] + self.radius,
51
- self.origin[1] + self.radius,
52
- self.radius, # Z extends upward from 0
53
- ]
54
- )
55
-
56
- return min_point, max_point
57
-
58
- def contains_point(self, point: np.ndarray) -> bool:
59
- """
60
- Check if a point lies within the sphere.
61
-
62
- Args:
63
- point: A 3D point to check
64
-
65
- Returns:
66
- bool: True if the point is inside the sphere, False otherwise
67
- """
68
- point = np.array(point)
69
- if point.shape != (3,):
70
- raise ValueError("Point must be a 3D point")
71
-
72
- # Calculate distance from center to point
73
- center_3d = self.center
74
- return np.linalg.norm(point - center_3d) <= self.radius
@@ -1,244 +0,0 @@
1
- import numpy as np
2
- from .boundary_conditions import _refecting_boundary, _absorbing_boundary
3
- from ...probabilityfuncs.markov_chain import MCMC_state_selection
4
-
5
- BOUNDARY_CONDITIONS = {
6
- "reflecting": _refecting_boundary,
7
- "absorbing": _absorbing_boundary,
8
- }
9
-
10
-
11
- class FBM_BP:
12
- """
13
- Fractional Brownian Motion (FBM) simulation with a Markov process for
14
- diffusion coefficients and Hurst exponents.
15
-
16
- This class simulates the motion of particles using a fractional Brownian motion model
17
- with adjustable parameters for diffusion and the Hurst exponent.
18
-
19
- Parameters:
20
- -----------
21
- n : int
22
- Number of time steps in the simulation.
23
- dt : float
24
- Time step duration in milliseconds.
25
- diffusion_parameters : np.ndarray
26
- Array of diffusion coefficients for the FBM simulation.
27
- hurst_parameters : np.ndarray
28
- Array of Hurst exponents for the FBM simulation.
29
- diffusion_parameter_transition_matrix : np.ndarray
30
- Transition matrix for diffusion coefficients.
31
- hurst_parameter_transition_matrix : np.ndarray
32
- Transition matrix for Hurst exponents.
33
- state_probability_diffusion : np.ndarray
34
- Initial probabilities of different diffusion states.
35
- state_probability_hurst : np.ndarray
36
- Initial probabilities of different Hurst states.
37
- space_lim : np.ndarray
38
- Space limits (min, max) for the FBM.
39
-
40
- Methods:
41
- --------
42
- _autocovariance(k: int, hurst: float) -> float:
43
- Computes the autocovariance function for fractional Gaussian noise (fGn).
44
-
45
- _setup() -> None:
46
- Sets up the simulation by precomputing the autocovariance matrix and initial states.
47
-
48
- fbm() -> np.ndarray:
49
- Runs the FBM simulation and returns the positions at each time step.
50
- """
51
-
52
- def __init__(
53
- self,
54
- n: int,
55
- dt: float,
56
- diffusion_parameters: np.ndarray,
57
- hurst_parameters: np.ndarray,
58
- diffusion_parameter_transition_matrix: np.ndarray,
59
- hurst_parameter_transition_matrix: np.ndarray,
60
- state_probability_diffusion: np.ndarray,
61
- state_probability_hurst: np.ndarray,
62
- space_lim: np.ndarray,
63
- ):
64
- self.n = int(n)
65
- self.dt = dt # ms
66
- self.diffusion_parameter = diffusion_parameters
67
- self.hurst_parameter = hurst_parameters
68
- # state probability of the diffusion parameter
69
- self.diffusion_parameter_transition_matrix = (
70
- diffusion_parameter_transition_matrix
71
- )
72
- # state probability of the hurst parameter
73
- self.hurst_parameter_transition_matrix = hurst_parameter_transition_matrix
74
- # probability of the initial state, this approximates the population distribution
75
- self.state_probability_diffusion = state_probability_diffusion
76
- # probability of the initial state, this approximates the population distribution
77
- self.state_probability_hurst = state_probability_hurst
78
- # space lim (min, max) for the FBM
79
- self.space_lim = np.array(space_lim, dtype=float)
80
- # initialize the autocovariance matrix and the diffusion parameter
81
- self._setup()
82
-
83
- def _autocovariance(self, k: int, hurst: float) -> float:
84
- """
85
- Autocovariance function for fractional Gaussian noise (fGn).
86
-
87
- Parameters:
88
- -----------
89
- k : int
90
- Lag in time steps.
91
- hurst : float
92
- Hurst parameter, which controls the roughness of the trajectory.
93
-
94
- Returns:
95
- --------
96
- float
97
- The autocovariance value for the given lag.
98
- """
99
- return 0.5 * (
100
- abs(k - 1) ** (2 * hurst)
101
- - 2 * abs(k) ** (2 * hurst)
102
- + abs(k + 1) ** (2 * hurst)
103
- )
104
-
105
- def _setup(self) -> None:
106
- """
107
- Precomputes the autocovariance matrix and sets up initial diffusion and Hurst parameters.
108
-
109
- This method initializes the state selection using Markov Chain Monte Carlo (MCMC)
110
- and avoids recomputation of the autocovariance matrix during the simulation.
111
- """
112
- self._cov = np.zeros(self.n)
113
- self._diff_a_n = np.zeros(self.n)
114
- self._hurst_n = np.zeros(self.n)
115
- # catch if the diffusion or hurst parameter sets are singular
116
- if len(self.diffusion_parameter) == 1:
117
- self._diff_a_n = np.full(self.n, self.diffusion_parameter[0])
118
- else:
119
- diff_a_start = np.random.choice(
120
- self.diffusion_parameter, p=self.state_probability_diffusion
121
- )
122
- self._diff_a_n[0] = diff_a_start
123
- self._diff_a_n[1:] = MCMC_state_selection(
124
- np.where(self.diffusion_parameter == diff_a_start)[0][0],
125
- self.diffusion_parameter_transition_matrix,
126
- self.diffusion_parameter,
127
- self.n - 1,
128
- )
129
-
130
- if len(self.hurst_parameter) == 1:
131
- self._hurst_n = np.full(self.n, self.hurst_parameter[0])
132
- else:
133
- hurst_start = np.random.choice(
134
- self.hurst_parameter, p=self.state_probability_hurst
135
- )
136
- self._hurst_n[0] = hurst_start
137
- self._hurst_n[1:] = MCMC_state_selection(
138
- np.where(self.hurst_parameter == hurst_start)[0][0],
139
- self.hurst_parameter_transition_matrix,
140
- self.hurst_parameter,
141
- self.n - 1,
142
- )
143
- for i in range(self.n):
144
- self._cov[i] = self._autocovariance(i, self._hurst_n[i])
145
-
146
- def fbm(self) -> np.ndarray:
147
- """
148
- Simulates fractional Brownian motion (FBM) over `n` time steps.
149
-
150
- If the Hurst exponent is 0.5 for all time steps, it performs a simple Gaussian
151
- random walk. Otherwise, it uses the precomputed autocovariance matrix to generate
152
- fractional Gaussian noise (fGn) and simulates FBM.
153
-
154
- Returns:
155
- --------
156
- np.ndarray
157
- An array representing the simulated FBM positions over time.
158
- """
159
- fgn = np.zeros(self.n)
160
- fbm_store = np.zeros(self.n)
161
- phi = np.zeros(self.n)
162
- psi = np.zeros(self.n)
163
- # construct a gaussian noise vector
164
- gn = (
165
- np.random.normal(0, 1, self.n)
166
- * np.sqrt(self.dt * 2 * self._diff_a_n)
167
- * (self.dt ** (2 * self._hurst_n))
168
- )
169
- # catch is all hurst are 0.5 then use the gaussian noise vector corresponding to the scale defined by the diffusion parameter
170
- if np.all(self._hurst_n == 0.5):
171
- # each gn is then pulled from a normal distribution with mean 0 and standard deviation diff_a_n
172
- # ignore the fbm calculations but keep the reflection
173
- for i in range(1, self.n):
174
- fbm_candidate = fbm_store[i - 1] + gn[i]
175
- # check if this is outside the space limit by using the reflecting boundary condition
176
- fbm_store[i] = _boundary_conditions(
177
- fbm_store[i - 1], fbm_candidate, self.space_lim, "reflecting"
178
- )
179
- return fbm_store
180
-
181
- fbm_store[0] = 0
182
- fgn[0] = gn[0]
183
- v = 1
184
- phi[0] = 0
185
-
186
- for i in range(1, self.n):
187
- phi[i - 1] = self._cov[i]
188
- for j in range(i - 1):
189
- psi[j] = phi[j]
190
- phi[i - 1] -= psi[j] * self._cov[i - j - 1]
191
- phi[i - 1] /= v
192
- for j in range(i - 1):
193
- phi[j] = psi[j] - phi[i - 1] * psi[i - j - 2]
194
- v *= 1 - phi[i - 1] * phi[i - 1]
195
- for j in range(i):
196
- fgn[i] += phi[j] * fgn[i - j - 1]
197
- fgn[i] += np.sqrt(np.abs(v)) * gn[i]
198
- # add to the fbm
199
- fbm_candidate = fbm_store[i - 1] + fgn[i]
200
-
201
- # check if this is outside the space limit by using the reflecting boundary condition
202
- fbm_store[i] = _boundary_conditions(
203
- fbm_store[i - 1], fbm_candidate, self.space_lim, "reflecting"
204
- )
205
- if fbm_store[i] != fbm_candidate:
206
- # update the fgn based on the new difference
207
- fgn[i] = fbm_store[i] - fbm_store[i - 1]
208
- return fbm_store
209
-
210
-
211
- def _boundary_conditions(
212
- fbm_store_last: float,
213
- fbm_candidate: float,
214
- space_lim: np.ndarray,
215
- condition_type: str,
216
- ) -> float:
217
- """
218
- Apply boundary conditions to the FBM simulation.
219
-
220
- Parameters:
221
- -----------
222
- fbm_store_last : float
223
- The last value of the fractional Brownian motion (FBM) trajectory.
224
- fbm_candidate : float
225
- The candidate value for the next step in the FBM trajectory.
226
- space_lim : np.ndarray
227
- A 2-element array representing the minimum and maximum space limits.
228
- condition_type : str
229
- The type of boundary condition to apply, either "reflecting" or "absorbing".
230
-
231
- Returns:
232
- --------
233
- float
234
- The new value for the FBM trajectory, adjusted by the specified boundary condition.
235
- """
236
- # check if the condition type is valid
237
- if condition_type not in BOUNDARY_CONDITIONS:
238
- raise ValueError(
239
- "Invalid condition type: "
240
- + condition_type
241
- + "! Must be one of: "
242
- + str(BOUNDARY_CONDITIONS.keys())
243
- )
244
- return BOUNDARY_CONDITIONS[condition_type](fbm_store_last, fbm_candidate, space_lim)