Skip to content

Commit 0fb973f

Browse files
committed
Update data types to float for direction angles and vectors
1 parent cc9bbd5 commit 0fb973f

File tree

4 files changed

+27
-26
lines changed

4 files changed

+27
-26
lines changed

src/ect/directions.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class Directions:
1616
Supports uniform, random, or custom sampling of directions.
1717
1818
**Examples:**
19-
19+
2020
.. code-block:: python
2121
2222
# Uniform sampling in 2D (default)
@@ -57,15 +57,15 @@ def __init__(
5757
- For 2D, directions are represented as angles; for higher dimensions, as unit vectors.
5858
- Use factory methods :meth:`uniform`, :meth:`random`, :meth:`from_angles`, or :meth:`from_vectors` for convenience.
5959
"""
60-
60+
6161
self.num_dirs = num_dirs
6262
self.sampling = sampling
6363
self.dim = dim
6464
self.endpoint = endpoint
6565

6666
self._rng = np.random.RandomState(seed)
67-
self._thetas = None
68-
self._vectors = None
67+
self._thetas: Optional[np.ndarray] = None
68+
self._vectors: Optional[np.ndarray] = None
6969
self._initialize_directions()
7070

7171
def _initialize_directions(self):
@@ -82,16 +82,14 @@ def _initialize_directions(self):
8282
else:
8383
# generate random normal samples and normalize to lie on the unit sphere
8484
self._vectors = self._rng.randn(self.num_dirs, self.dim)
85-
self._vectors /= np.linalg.norm(self._vectors,
86-
axis=1, keepdims=True)
85+
self._vectors /= np.linalg.norm(self._vectors, axis=1, keepdims=True)
8786
elif self.sampling == Sampling.RANDOM:
8887
if self.dim == 2:
8988
self._thetas = self._rng.uniform(0, 2 * np.pi, self.num_dirs)
9089
self._thetas.sort()
9190
else:
9291
self._vectors = self._rng.randn(self.num_dirs, self.dim)
93-
self._vectors /= np.linalg.norm(self._vectors,
94-
axis=1, keepdims=True)
92+
self._vectors /= np.linalg.norm(self._vectors, axis=1, keepdims=True)
9593

9694
@classmethod
9795
def uniform(
@@ -142,11 +140,11 @@ def from_angles(cls, angles: Sequence[float]) -> "Directions":
142140
- Angles are stored in :attr:`thetas` and unit vectors are computed as needed.
143141
"""
144142
instance = cls(len(angles), Sampling.CUSTOM, dim=2)
145-
instance._thetas = np.array(angles)
143+
instance._thetas = np.array(angles, dtype=float)
146144
return instance
147145

148146
@classmethod
149-
def from_vectors(cls, vectors: Sequence[tuple]) -> "Directions":
147+
def from_vectors(cls, vectors: Sequence[Sequence[float]]) -> "Directions":
150148
"""
151149
Create a Directions instance from custom direction vectors in any dimension.
152150
@@ -163,12 +161,12 @@ def from_vectors(cls, vectors: Sequence[tuple]) -> "Directions":
163161
- Vectors are normalized to unit length.
164162
- For 2D, angles are computed from the vectors and available via :attr:`thetas`.
165163
"""
166-
vectors = np.array(vectors, dtype=float)
167-
norms = np.linalg.norm(vectors, axis=1, keepdims=True)
164+
vectors_arr = np.array(vectors, dtype=float)
165+
norms = np.linalg.norm(vectors_arr, axis=1, keepdims=True)
168166
if np.any(norms == 0):
169167
raise ValueError("Zero-magnitude vectors are not allowed")
170-
normalized = vectors / norms
171-
instance = cls(len(vectors), Sampling.CUSTOM, dim=vectors.shape[1])
168+
normalized = vectors_arr / norms
169+
instance = cls(len(vectors_arr), Sampling.CUSTOM, dim=vectors_arr.shape[1])
172170
instance._vectors = normalized
173171
if instance.dim == 2:
174172
instance._thetas = np.arctan2(normalized[:, 1], normalized[:, 0])
@@ -196,6 +194,7 @@ def thetas(self) -> np.ndarray:
196194
if self._thetas is None:
197195
# Compute the angles from the vectors.
198196
self._thetas = np.arctan2(self.vectors[:, 1], self.vectors[:, 0])
197+
assert self._thetas is not None
199198
return self._thetas
200199

201200
@property
@@ -215,13 +214,15 @@ def vectors(self) -> np.ndarray:
215214
"""
216215
if self._vectors is None:
217216
if self.dim == 2:
218-
self._vectors = np.column_stack(
219-
(np.cos(self._thetas), np.sin(self._thetas))
220-
)
217+
thetas = self._thetas
218+
if thetas is None:
219+
raise ValueError("2D direction angles are not initialized.")
220+
self._vectors = np.column_stack((np.cos(thetas), np.sin(thetas)))
221221
else:
222222
raise ValueError(
223223
"Direction vectors for dimensions >2 should be generated during initialization."
224224
)
225+
assert self._vectors is not None
225226
return self._vectors
226227

227228
def __len__(self) -> int:

src/ect/sect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from .embed_complex import EmbeddedComplex
33
from .directions import Directions
44
from .results import ECTResult
5-
from typing import Optional, Union
5+
from typing import Optional
66
import numpy as np
77

88

src/ect/validation/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def validate(
8181
all_coords: np.ndarray,
8282
cell_indices: List[int],
8383
all_indices: List[int],
84-
dim: int = None,
84+
dim: Optional[int] = None,
8585
) -> ValidationResult:
8686
"""
8787
Validate a cell against this rule.

src/ect/validation/rules.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def validate(
4040
all_coords: np.ndarray,
4141
cell_indices: List[int],
4242
all_indices: List[int],
43-
dim: int = None,
43+
dim: Optional[int] = None,
4444
) -> ValidationResult:
4545
"""Validate that dimension is non-negative."""
4646
if dim is not None and dim < 0:
@@ -68,7 +68,7 @@ def validate(
6868
all_coords: np.ndarray,
6969
cell_indices: List[int],
7070
all_indices: List[int],
71-
dim: int = None,
71+
dim: Optional[int] = None,
7272
) -> ValidationResult:
7373
"""Validate vertex count matches cell dimension requirements."""
7474
if dim is None:
@@ -114,7 +114,7 @@ def validate(
114114
all_coords: np.ndarray,
115115
cell_indices: List[int],
116116
all_indices: List[int],
117-
dim: int = None,
117+
dim: Optional[int] = None,
118118
) -> ValidationResult:
119119
"""Validate coordinate dimensions are consistent."""
120120
if self.dimension_checker is None or cell_coords is None:
@@ -156,7 +156,7 @@ def validate(
156156
all_coords: np.ndarray,
157157
cell_indices: List[int],
158158
all_indices: List[int],
159-
dim: int = None,
159+
dim: Optional[int] = None,
160160
) -> ValidationResult:
161161
"""Validate that no other vertices lie on this edge's interior."""
162162
is_valid, error_msg = validate_edge_embedding(
@@ -185,7 +185,7 @@ def validate(
185185
all_coords: np.ndarray,
186186
cell_indices: List[int],
187187
all_indices: List[int],
188-
dim: int = None,
188+
dim: Optional[int] = None,
189189
) -> ValidationResult:
190190
"""Validate that no other vertices lie inside this face."""
191191
is_valid, error_msg = validate_face_embedding(
@@ -214,7 +214,7 @@ def validate(
214214
all_coords: np.ndarray,
215215
cell_indices: List[int],
216216
all_indices: List[int],
217-
dim: int = None,
217+
dim: Optional[int] = None,
218218
) -> ValidationResult:
219219
"""Validate that face edges don't intersect each other"""
220220

@@ -275,7 +275,7 @@ def validate(
275275
all_coords: np.ndarray,
276276
cell_indices: List[int],
277277
all_indices: List[int],
278-
dim: int = None,
278+
dim: Optional[int] = None,
279279
) -> ValidationResult:
280280
"""Validate that all boundary edges exist for this face."""
281281
if self.edge_checker is None:

0 commit comments

Comments
 (0)