From bcf4f21b1a740516c502fc87d87353821990fd9f Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 18:13:34 -0300 Subject: [PATCH 01/64] Create CHANGELOG.md --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..40321fe --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,27 @@ +# Changelog + +All notable changes to this project are documented in this file. + +This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and uses [Semantic Versioning](https://semver.org/). + +--- + +## [Unreleased] + +### Added +- MIT license/authorship headers to all `.py` files ([features/cuda]) +- CUDA feature branch for GPU support + +### Changed +- Restructured branch strategy: `main`, `dev`, `features/cuda` +- Clarified setup instructions in `README.md` + +--- + +## [0.1.0] – 2025-05-14 + +### Added +- Initial codebase and Git setup +- Created branches: `main`, `dev`, and `feature/cuda` +- Basic Python project scaffold From 8353ccca95cc8fa639d86abd79bc229eee324d23 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 19:31:00 -0300 Subject: [PATCH 02/64] Create ph.py --- spinstep/utils/ph.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 spinstep/utils/ph.py diff --git a/spinstep/utils/ph.py b/spinstep/utils/ph.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/spinstep/utils/ph.py @@ -0,0 +1 @@ + From 91d0f5d95557c8b99f297e083320cb7a4191ca2f Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 19:32:10 -0300 Subject: [PATCH 03/64] Add files via upload --- spinstep/utils/array_backend.py | 13 +++++++++++++ spinstep/utils/quaternion_math.py | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 spinstep/utils/array_backend.py create mode 100644 spinstep/utils/quaternion_math.py diff --git a/spinstep/utils/array_backend.py b/spinstep/utils/array_backend.py new file mode 100644 index 0000000..fd11cad --- /dev/null +++ b/spinstep/utils/array_backend.py @@ -0,0 +1,13 @@ +# array_backend.py — MIT License +# Author: Eraldo B. Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. + +def get_array_module(use_cuda=False): + if use_cuda: + try: + import cupy as cp + return cp + except ImportError: + print("[SpinStep] CuPy not found, falling back to NumPy.") + import numpy as np + return np diff --git a/spinstep/utils/quaternion_math.py b/spinstep/utils/quaternion_math.py new file mode 100644 index 0000000..ad6338c --- /dev/null +++ b/spinstep/utils/quaternion_math.py @@ -0,0 +1,16 @@ +# quaternion_math.py — MIT License +# Author: Eraldo B. Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. + +def batch_quaternion_angle(qs1, qs2, xp): + """ + qs1: (N, 4) array + qs2: (M, 4) array + xp: array module (np or cp) + Returns (N, M) array of angular distances. + """ + # Quaternion inner product: angle = 2*arccos(|dot(q1, q2)|) + dots = xp.abs(xp.dot(qs1, qs2.T)) + dots = xp.clip(dots, -1.0, 1.0) + angles = 2 * xp.arccos(dots) + return angles From 8d70b531c04aa7931ba93d8bc26e66f5f8f0d648 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 19:33:23 -0300 Subject: [PATCH 04/64] Delete spinstep/utils/ph.py --- spinstep/utils/ph.py | 1 - 1 file changed, 1 deletion(-) delete mode 100644 spinstep/utils/ph.py diff --git a/spinstep/utils/ph.py b/spinstep/utils/ph.py deleted file mode 100644 index 8b13789..0000000 --- a/spinstep/utils/ph.py +++ /dev/null @@ -1 +0,0 @@ - From 802584445b4ce8d0999c7c8db20126864fd84f8c Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 19:49:17 -0300 Subject: [PATCH 05/64] Update discrete.py --- spinstep/discrete.py | 62 +++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/spinstep/discrete.py b/spinstep/discrete.py index d54b520..fe6b0c3 100644 --- a/spinstep/discrete.py +++ b/spinstep/discrete.py @@ -1,36 +1,56 @@ -import numpy as np -from scipy.spatial.transform import Rotation as R -from sklearn.neighbors import BallTree +from spinstep.utils.array_backend import get_array_module class DiscreteOrientationSet: - def __init__(self, orientations): - arr = np.array(orientations) + def __init__(self, orientations, use_cuda=False): + xp = get_array_module(use_cuda) + arr = xp.array(orientations) if arr.ndim != 2 or arr.shape[1] != 4: raise ValueError("Each orientation must be a quaternion [x, y, z, w]") - norms = np.linalg.norm(arr, axis=1) - if np.any(norms < 1e-8): + norms = xp.linalg.norm(arr, axis=1) + if xp.any(norms < 1e-8): raise ValueError("Zero or near-zero quaternion in orientation set") arr = arr / norms[:, None] self.orientations = arr + self.xp = xp + self.use_cuda = use_cuda - # Precompute rotation vectors for BallTree - self.rotvecs = R.from_quat(arr).as_rotvec() - if len(arr) > 100: - self._balltree = BallTree(self.rotvecs) - else: - self._balltree = None + # Only for CPU/NumPy mode: BallTree for fast queries + self._balltree = None + if not use_cuda: + from scipy.spatial.transform import Rotation as R + from sklearn.neighbors import BallTree + self.rotvecs = R.from_quat(arr).as_rotvec() + if len(arr) > 100: + self._balltree = BallTree(self.rotvecs) + else: + self._balltree = None def query_within_angle(self, quat, angle): """Return indices of orientations within the given angle of quat.""" - rv = R.from_quat(quat).as_rotvec().reshape(1, -1) - if self._balltree is not None: - # BallTree in rotation vector space, Euclidean distance ≈ angle for small rotations - inds = self._balltree.query_radius(rv, r=angle)[0] + if self.use_cuda: + # Brute force: batch math on GPU + # Convert quat to rotvec on CPU, then broadcast to GPU + import numpy as np + from scipy.spatial.transform import Rotation as R + rv = R.from_quat(np.array(quat)).as_rotvec().reshape(1, -1) + rv_gpu = self.xp.array(rv) + dists = self.xp.linalg.norm(self.orientations - rv_gpu, axis=1) + inds = self.xp.where(dists < angle)[0] + return inds else: - # Brute force for small sets - dists = np.linalg.norm(self.rotvecs - rv, axis=1) - inds = np.where(dists < angle)[0] - return inds + from scipy.spatial.transform import Rotation as R + rv = R.from_quat(quat).as_rotvec().reshape(1, -1) + if self._balltree is not None: + inds = self._balltree.query_radius(rv, r=angle)[0] + else: + dists = self.xp.linalg.norm(self.rotvecs - rv, axis=1) + inds = self.xp.where(dists < angle)[0] + return inds + + def as_numpy(self): + if self.use_cuda: + return self.xp.asnumpy(self.orientations) + return self.orientations # ... rest unchanged ... From 86b03879a2240067fdb27360299ba37be6e00d4f Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 19:51:53 -0300 Subject: [PATCH 06/64] Update discrete.py --- spinstep/discrete.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spinstep/discrete.py b/spinstep/discrete.py index fe6b0c3..58f2899 100644 --- a/spinstep/discrete.py +++ b/spinstep/discrete.py @@ -1,3 +1,7 @@ +# discrete.py — MIT License +# Author: Eraldo B. Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. + from spinstep.utils.array_backend import get_array_module class DiscreteOrientationSet: From dc3563a5591e3fdaa02474b218c715e600ccc4ac Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 20:05:57 -0300 Subject: [PATCH 07/64] Update __init.py__ --- spinstep/__init.py__ | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spinstep/__init.py__ b/spinstep/__init.py__ index 8442e36..c0cc448 100644 --- a/spinstep/__init.py__ +++ b/spinstep/__init.py__ @@ -1,3 +1,7 @@ +# __init__.py — MIT License +# Author: Eraldo B. Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. + from .node import Node from .traversal import QuaternionDepthIterator from .discrete import DiscreteOrientationSet From f6cd07ac1a6618805fac0b4a5bcfaf1d5eccac0c Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 20:09:13 -0300 Subject: [PATCH 08/64] Update demo.py --- spinstep/demo.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spinstep/demo.py b/spinstep/demo.py index 02e1e7a..8b96118 100644 --- a/spinstep/demo.py +++ b/spinstep/demo.py @@ -1,3 +1,7 @@ +# demo.py — MIT License +# Author: Eraldo Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. + from spinstep.node import Node from spinstep.traversal import QuaternionDepthIterator from spinstep.quaternion_utils import quaternion_from_euler @@ -7,8 +11,6 @@ # .Applies a quaternion-based depth-first traversal. # .Only visits nodes that lie within a given angular threshold (like aiming a "cone" of rotation). - Only visits nodes that lie within a given angular threshold (like aiming a "cone" of rotation). - def build_demo_tree(): """Build a small tree with varied 3D orientations (yaw, pitch, roll).""" root = Node("root", [0, 0, 0, 1], [ From 7cea844b71b90da54192c654eabfb01b418ea999 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 20:11:03 -0300 Subject: [PATCH 09/64] Update demo1_tree_traversal.py --- spinstep/demo1_tree_traversal.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spinstep/demo1_tree_traversal.py b/spinstep/demo1_tree_traversal.py index 40285f0..789b16a 100644 --- a/spinstep/demo1_tree_traversal.py +++ b/spinstep/demo1_tree_traversal.py @@ -1,3 +1,7 @@ +# demo1_tree_traversal.py — MIT License +# Author: Eraldo Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. + import numpy as np from scipy.spatial.transform import Rotation as R From 3753073a1d896bea787084a1d1d2673c56369178 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 20:13:16 -0300 Subject: [PATCH 10/64] Update demo2_full_depth_traversal.py --- spinstep/demo2_full_depth_traversal.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spinstep/demo2_full_depth_traversal.py b/spinstep/demo2_full_depth_traversal.py index 959926e..c632e07 100644 --- a/spinstep/demo2_full_depth_traversal.py +++ b/spinstep/demo2_full_depth_traversal.py @@ -1,3 +1,7 @@ +# demo2_full_depth_traversal.py — MIT License +# Author: Eraldo Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. + import numpy as np from scipy.spatial.transform import Rotation as R From 482540ac927783438d0d13376c101e47c2c438e3 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 20:14:13 -0300 Subject: [PATCH 11/64] Update demo3_spatial_traversal.py --- spinstep/demo3_spatial_traversal.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spinstep/demo3_spatial_traversal.py b/spinstep/demo3_spatial_traversal.py index 5fe0756..320f262 100644 --- a/spinstep/demo3_spatial_traversal.py +++ b/spinstep/demo3_spatial_traversal.py @@ -1,3 +1,7 @@ +# demo3_spatial_traversal.py — MIT License +# Author: Eraldo Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. + import numpy as np from scipy.spatial.transform import Rotation as R From ddf88a6a96516c35cdb28baec2dabf169be2225d Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 20:15:50 -0300 Subject: [PATCH 12/64] Update demo4_discrete_traversal.py --- spinstep/demo4_discrete_traversal.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spinstep/demo4_discrete_traversal.py b/spinstep/demo4_discrete_traversal.py index 33ab863..f67080c 100644 --- a/spinstep/demo4_discrete_traversal.py +++ b/spinstep/demo4_discrete_traversal.py @@ -1,3 +1,7 @@ +# demo4_discrete_traversal.py — MIT License +# Author: Eraldo Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. + import numpy as np from scipy.spatial.transform import Rotation as R from spinstep.node import Node @@ -25,4 +29,4 @@ visited_nodes = [] for node in iterator: visited_nodes.append(node.name) - print("Visited:", node.name) \ No newline at end of file + print("Visited:", node.name) From 0a9862fbe7e010282901550e9d6d8ee00c7a4a18 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 20:17:21 -0300 Subject: [PATCH 13/64] Update discrete_iterator.py --- spinstep/discrete_iterator.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spinstep/discrete_iterator.py b/spinstep/discrete_iterator.py index 2a37334..d6fc61a 100644 --- a/spinstep/discrete_iterator.py +++ b/spinstep/discrete_iterator.py @@ -1,3 +1,7 @@ +# discrete_iterator.py — MIT License +# Author: Eraldo Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. + import numpy as np from scipy.spatial.transform import Rotation as R From eb27c571301a10dbc040fe9312c5b326a59b44d5 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 20:19:18 -0300 Subject: [PATCH 14/64] Update node.py --- spinstep/node.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spinstep/node.py b/spinstep/node.py index 83f6a13..eb8bd73 100644 --- a/spinstep/node.py +++ b/spinstep/node.py @@ -1,3 +1,7 @@ +# demo1_tree_traversal.py — MIT License +# Author: Eraldo Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. + import numpy as np class Node: From cdc884698656df65895fb55c0c2decd9599f8b62 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 20:21:35 -0300 Subject: [PATCH 15/64] Update quaternion_utils.py --- spinstep/quaternion_utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spinstep/quaternion_utils.py b/spinstep/quaternion_utils.py index 4d4d836..2c01c7c 100644 --- a/spinstep/quaternion_utils.py +++ b/spinstep/quaternion_utils.py @@ -1,3 +1,7 @@ +# quaternion_utils.py — MIT License +# Author: Eraldo Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. + import numpy as np from scipy.spatial.transform import Rotation as R From 383782999165c06d0cc7923758eea75955d07f65 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 20:22:13 -0300 Subject: [PATCH 16/64] Update node.py --- spinstep/node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spinstep/node.py b/spinstep/node.py index eb8bd73..8b4d545 100644 --- a/spinstep/node.py +++ b/spinstep/node.py @@ -1,4 +1,4 @@ -# demo1_tree_traversal.py — MIT License +# node.py — MIT License # Author: Eraldo Marques — Created: 2025-05-14 # See LICENSE.txt for full terms. This header must be retained in redistributions. From 39ecfd6e4e85b5cd230bba47a303c3093c8986ea Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 20:22:57 -0300 Subject: [PATCH 17/64] Update traversal.py --- spinstep/traversal.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spinstep/traversal.py b/spinstep/traversal.py index 3ac3b78..bd75dfa 100644 --- a/spinstep/traversal.py +++ b/spinstep/traversal.py @@ -1,3 +1,7 @@ +# traversal.py — MIT License +# Author: Eraldo Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. + from scipy.spatial.transform import Rotation as R class QuaternionDepthIterator: From a4c98fa3571275fb410663940abc3d3eed68f1e5 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Wed, 14 May 2025 20:26:10 -0300 Subject: [PATCH 18/64] Update test.py --- tests/test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test.py b/tests/test.py index 8b13789..2e4eccf 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1 +1,3 @@ - +# test.py — MIT License +# Author: Eraldo Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. From 3d2ed583d362e919c5d55c75e0ed2cb34e8680cd Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:23:16 -0300 Subject: [PATCH 19/64] Add files via upload --- docs/docs_07_troubleshooting_Version2.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 docs/docs_07_troubleshooting_Version2.md diff --git a/docs/docs_07_troubleshooting_Version2.md b/docs/docs_07_troubleshooting_Version2.md new file mode 100644 index 0000000..ab2e8d1 --- /dev/null +++ b/docs/docs_07_troubleshooting_Version2.md @@ -0,0 +1,20 @@ +# Troubleshooting & FAQ + +## Why is my traversal slow? +- Check the size of your DiscreteOrientationSet. Very fine grids may result in millions of steps. +- Try reducing the number of points or increasing `angle_threshold`. +- For large sets, consider spatial pruning or batching. + +## I got a ValueError about quaternions? +- All quaternions must be normalized and non-zero. Use the provided utilities or `scipy.spatial.transform.Rotation` to generate valid quaternions. + +## What if my node doesn't have `.orientation` or `.children`? +- All nodes must implement both attributes. If you use custom node types, subclass or adapt as needed. + +## Can I use Euler angles or axis-angle? +- Yes! Convert to quaternions using `scipy.spatial.transform.Rotation`. + +## What is a "closure property" test? +- For symmetry groups, applying all group elements in sequence should bring you (numerically) back to a group element—verifying group structure. + +--- \ No newline at end of file From b051c231a60e18ea14df6246355ce98fac3c879b Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:25:42 -0300 Subject: [PATCH 20/64] Rename docs_07_troubleshooting_Version2.md to 07_troubleshooting.md --- ...ocs_07_troubleshooting_Version2.md => 07_troubleshooting.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename docs/{docs_07_troubleshooting_Version2.md => 07_troubleshooting.md} (99%) diff --git a/docs/docs_07_troubleshooting_Version2.md b/docs/07_troubleshooting.md similarity index 99% rename from docs/docs_07_troubleshooting_Version2.md rename to docs/07_troubleshooting.md index ab2e8d1..8bd9fa1 100644 --- a/docs/docs_07_troubleshooting_Version2.md +++ b/docs/07_troubleshooting.md @@ -17,4 +17,4 @@ ## What is a "closure property" test? - For symmetry groups, applying all group elements in sequence should bring you (numerically) back to a group element—verifying group structure. ---- \ No newline at end of file +--- From 09ef68db666b96da2053c5b871797972dd4f3191 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:28:28 -0300 Subject: [PATCH 21/64] Add files via upload --- docs/docs_api_Version2.md | 137 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 docs/docs_api_Version2.md diff --git a/docs/docs_api_Version2.md b/docs/docs_api_Version2.md new file mode 100644 index 0000000..3e8212b --- /dev/null +++ b/docs/docs_api_Version2.md @@ -0,0 +1,137 @@ +# SpinStep API Reference + +This document details the main public API of the SpinStep library. +For usage examples, see the [Examples & Tutorials](examples.md). + +--- + +## Table of Contents + +- [spinstep.node.Node](#spinstepnodenode) +- [spinstep.discrete.DiscreteOrientationSet](#spinstepdiscretediscreteorientationset) +- [spinstep.discrete_iterator.DiscreteQuaternionIterator](#spinstepdiscrete_iteratordiscretequaternioniterator) +- [spinstep.continuous.QuaternionDepthIterator](#spinstepcontinuousquaterniondepthiterator) +- [Exceptions](#exceptions) + +--- + +## spinstep.node.Node + +```python +class Node: + def __init__(self, name: str, orientation: Sequence[float], children: Optional[Iterable["Node"]] = None) +``` + +- **name** (`str`): Node identifier (any string). +- **orientation** (`[x, y, z, w]`): Quaternion representing the orientation. Automatically normalized. +- **children** (`Iterable[Node]`, optional): List or iterable of child nodes. + +#### Attributes + +- **name**: Node name. +- **orientation**: Normalized quaternion (`numpy.ndarray`, shape `(4,)`). +- **children**: List of child nodes. + +--- + +## spinstep.discrete.DiscreteOrientationSet + +A container for a set of discrete orientations (quaternions). + +```python +class DiscreteOrientationSet: + def __init__(self, orientations: Sequence[Sequence[float]]) +``` + +#### Constructor + +- **orientations**: List or array of normalized quaternions (`[x, y, z, w]`). + +#### Class Methods + +- `from_cube()`: Returns a set of cube group orientations (24 elements). +- `from_icosahedron()`: Returns a set of icosahedral group orientations (60 elements). +- `from_custom(orientations)`: Create from user-specified list of quaternions. +- `from_sphere_grid(N: int)`: Approximate a uniform grid of `N` orientations on the sphere. + +#### Methods + +- `query_within_angle(quat: Sequence[float], angle: float) -> np.ndarray`: + Returns indices of orientations within `angle` (radians) of `quat`. + +#### Attributes + +- **orientations**: Array of normalized quaternions (shape `(n,4)`). + +--- + +## spinstep.discrete_iterator.DiscreteQuaternionIterator + +Depth-first traversal over a tree of Nodes using a discrete orientation set. + +```python +class DiscreteQuaternionIterator: + def __init__( + self, + start_node: Node, + orientation_set: DiscreteOrientationSet, + angle_threshold: float = np.pi/8, + max_depth: int = 100 + ) +``` + +- **start_node**: The root `Node` to start traversal from. +- **orientation_set**: Instance of `DiscreteOrientationSet`. +- **angle_threshold**: Maximum allowed angular distance (in radians) to consider two orientations "matching." +- **max_depth**: Maximum recursion depth. + +#### Usage + +```python +it = DiscreteQuaternionIterator(root_node, orientation_set, angle_threshold=0.2) +for node in it: + print(node.name) +``` + +--- + +## spinstep.continuous.QuaternionDepthIterator + +Depth-first traversal for continuous (non-discrete) orientation search. + +```python +class QuaternionDepthIterator: + def __init__( + self, + start_node: Node, + angle_threshold: float = np.pi/8, + max_depth: int = 100 + ) +``` + +- **start_node**: The root `Node`. +- **angle_threshold**: Maximum allowed angular distance (in radians) for matching. +- **max_depth**: Maximum recursion depth. + +#### Usage + +```python +it = QuaternionDepthIterator(root_node, angle_threshold=0.1) +for node in it: + print(node.name) +``` + +--- + +## Exceptions + +- **ValueError**: Raised for invalid or non-normalized quaternions, or malformed orientation sets. +- **AttributeError**: Raised if a node lacks required `.orientation` or `.children` attributes. + +--- + +## See Also + +- [Orientation Sets](05_orientation_sets.md) +- [Discrete Traversal Guide](06_discrete_traversal.md) +- [Troubleshooting & FAQ](07_troubleshooting.md) \ No newline at end of file From 0948c95d58b8ef2d5818195f1d3d4a5e0222c674 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:30:15 -0300 Subject: [PATCH 22/64] Rename docs_api_Version2.md to 08_API_Reference.md --- docs/{docs_api_Version2.md => 08_API_Reference.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename docs/{docs_api_Version2.md => 08_API_Reference.md} (98%) diff --git a/docs/docs_api_Version2.md b/docs/08_API_Reference.md similarity index 98% rename from docs/docs_api_Version2.md rename to docs/08_API_Reference.md index 3e8212b..1b9d465 100644 --- a/docs/docs_api_Version2.md +++ b/docs/08_API_Reference.md @@ -134,4 +134,4 @@ for node in it: - [Orientation Sets](05_orientation_sets.md) - [Discrete Traversal Guide](06_discrete_traversal.md) -- [Troubleshooting & FAQ](07_troubleshooting.md) \ No newline at end of file +- [Troubleshooting & FAQ](07_troubleshooting.md) From 079acbb6ab71a069ee24dfaa0a7e5ea59ed6757b Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:39:38 -0300 Subject: [PATCH 23/64] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 83030ec..1a6d0d6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SpinStep +# SpinStep - [Read the Docs]( **SpinStep** is a proof-of-concept quaternion-driven traversal framework for trees and orientation-based data structures. From a6723c4f68470f5640a9ab0655aa73e9fd212177 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:46:44 -0300 Subject: [PATCH 24/64] Create ph --- examples/ph | 1 + 1 file changed, 1 insertion(+) create mode 100644 examples/ph diff --git a/examples/ph b/examples/ph new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/ph @@ -0,0 +1 @@ + From 86078c4aa9eba95d66feeea4dc809a9ba0d33cdf Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:47:59 -0300 Subject: [PATCH 25/64] Add files via upload --- examples/docs_08_cuda_support_Version2.md | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 examples/docs_08_cuda_support_Version2.md diff --git a/examples/docs_08_cuda_support_Version2.md b/examples/docs_08_cuda_support_Version2.md new file mode 100644 index 0000000..ccf6578 --- /dev/null +++ b/examples/docs_08_cuda_support_Version2.md @@ -0,0 +1,31 @@ +# CUDA (GPU) Support in SpinStep + +SpinStep supports optional GPU acceleration for batch orientation math using [CuPy](https://cupy.dev/). + +## Usage + +```python +from spinstep.discrete import DiscreteOrientationSet +set_gpu = DiscreteOrientationSet(orientations, use_cuda=True) +``` + +If CuPy or a compatible GPU is not found, SpinStep will fall back to CPU (NumPy) and print a warning. + +## Requirements + +- [CuPy](https://cupy.dev/) (`pip install cupy`) +- NVIDIA GPU with CUDA drivers + +## Accelerated Features + +- Batch orientation storage and math +- Fast angular distance computations for large orientation sets + +## Limitations + +- Tree traversal logic remains on CPU (Python object graph) +- Only orientation math is GPU-accelerated + +## Example + +See [`examples/gpu_orientation_matching.py`](../examples/gpu_orientation_matching.py) \ No newline at end of file From 83e71a2473c3f2de72ee5ae21142b393d4ec718a Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:48:32 -0300 Subject: [PATCH 26/64] Delete examples/ph --- examples/ph | 1 - 1 file changed, 1 deletion(-) delete mode 100644 examples/ph diff --git a/examples/ph b/examples/ph deleted file mode 100644 index 8b13789..0000000 --- a/examples/ph +++ /dev/null @@ -1 +0,0 @@ - From d85413d9dcc7f45372a71d726080fdf1a1e0ebb0 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:49:42 -0300 Subject: [PATCH 27/64] Rename docs_08_cuda_support_Version2.md to 10_cuda_support.md --- .../{docs_08_cuda_support_Version2.md => 10_cuda_support.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename examples/{docs_08_cuda_support_Version2.md => 10_cuda_support.md} (97%) diff --git a/examples/docs_08_cuda_support_Version2.md b/examples/10_cuda_support.md similarity index 97% rename from examples/docs_08_cuda_support_Version2.md rename to examples/10_cuda_support.md index ccf6578..07813da 100644 --- a/examples/docs_08_cuda_support_Version2.md +++ b/examples/10_cuda_support.md @@ -28,4 +28,4 @@ If CuPy or a compatible GPU is not found, SpinStep will fall back to CPU (NumPy) ## Example -See [`examples/gpu_orientation_matching.py`](../examples/gpu_orientation_matching.py) \ No newline at end of file +See [`examples/gpu_orientation_matching.py`](../examples/gpu_orientation_matching.py) From 757fc1c04dd783d9f9e43bb2d371ecec65863a74 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:50:35 -0300 Subject: [PATCH 28/64] Delete examples/10_cuda_support.md --- examples/10_cuda_support.md | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 examples/10_cuda_support.md diff --git a/examples/10_cuda_support.md b/examples/10_cuda_support.md deleted file mode 100644 index 07813da..0000000 --- a/examples/10_cuda_support.md +++ /dev/null @@ -1,31 +0,0 @@ -# CUDA (GPU) Support in SpinStep - -SpinStep supports optional GPU acceleration for batch orientation math using [CuPy](https://cupy.dev/). - -## Usage - -```python -from spinstep.discrete import DiscreteOrientationSet -set_gpu = DiscreteOrientationSet(orientations, use_cuda=True) -``` - -If CuPy or a compatible GPU is not found, SpinStep will fall back to CPU (NumPy) and print a warning. - -## Requirements - -- [CuPy](https://cupy.dev/) (`pip install cupy`) -- NVIDIA GPU with CUDA drivers - -## Accelerated Features - -- Batch orientation storage and math -- Fast angular distance computations for large orientation sets - -## Limitations - -- Tree traversal logic remains on CPU (Python object graph) -- Only orientation math is GPU-accelerated - -## Example - -See [`examples/gpu_orientation_matching.py`](../examples/gpu_orientation_matching.py) From 6e553ae084903fac872989b3914b944cbb7fdc4e Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:51:23 -0300 Subject: [PATCH 29/64] Add files via upload --- docs/docs_08_cuda_support_Version2.md | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 docs/docs_08_cuda_support_Version2.md diff --git a/docs/docs_08_cuda_support_Version2.md b/docs/docs_08_cuda_support_Version2.md new file mode 100644 index 0000000..ccf6578 --- /dev/null +++ b/docs/docs_08_cuda_support_Version2.md @@ -0,0 +1,31 @@ +# CUDA (GPU) Support in SpinStep + +SpinStep supports optional GPU acceleration for batch orientation math using [CuPy](https://cupy.dev/). + +## Usage + +```python +from spinstep.discrete import DiscreteOrientationSet +set_gpu = DiscreteOrientationSet(orientations, use_cuda=True) +``` + +If CuPy or a compatible GPU is not found, SpinStep will fall back to CPU (NumPy) and print a warning. + +## Requirements + +- [CuPy](https://cupy.dev/) (`pip install cupy`) +- NVIDIA GPU with CUDA drivers + +## Accelerated Features + +- Batch orientation storage and math +- Fast angular distance computations for large orientation sets + +## Limitations + +- Tree traversal logic remains on CPU (Python object graph) +- Only orientation math is GPU-accelerated + +## Example + +See [`examples/gpu_orientation_matching.py`](../examples/gpu_orientation_matching.py) \ No newline at end of file From 9d5ca69ef41fbbd9bffe02fbf99224b4e926b3a0 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:52:05 -0300 Subject: [PATCH 30/64] Rename 08_API_Reference.md to 09_API_Reference.md --- docs/{08_API_Reference.md => 09_API_Reference.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{08_API_Reference.md => 09_API_Reference.md} (100%) diff --git a/docs/08_API_Reference.md b/docs/09_API_Reference.md similarity index 100% rename from docs/08_API_Reference.md rename to docs/09_API_Reference.md From 098b09e366785e651b29c1cdcf0800ff200e3510 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:52:42 -0300 Subject: [PATCH 31/64] Rename docs_08_cuda_support_Version2.md to 09_cuda_support.md --- docs/{docs_08_cuda_support_Version2.md => 09_cuda_support.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename docs/{docs_08_cuda_support_Version2.md => 09_cuda_support.md} (97%) diff --git a/docs/docs_08_cuda_support_Version2.md b/docs/09_cuda_support.md similarity index 97% rename from docs/docs_08_cuda_support_Version2.md rename to docs/09_cuda_support.md index ccf6578..07813da 100644 --- a/docs/docs_08_cuda_support_Version2.md +++ b/docs/09_cuda_support.md @@ -28,4 +28,4 @@ If CuPy or a compatible GPU is not found, SpinStep will fall back to CPU (NumPy) ## Example -See [`examples/gpu_orientation_matching.py`](../examples/gpu_orientation_matching.py) \ No newline at end of file +See [`examples/gpu_orientation_matching.py`](../examples/gpu_orientation_matching.py) From 315f58250d99cfd968fdb388ef3e2028a9cd69d4 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:53:07 -0300 Subject: [PATCH 32/64] Rename 09_cuda_support.md to 08_cuda_support.md --- docs/{09_cuda_support.md => 08_cuda_support.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{09_cuda_support.md => 08_cuda_support.md} (100%) diff --git a/docs/09_cuda_support.md b/docs/08_cuda_support.md similarity index 100% rename from docs/09_cuda_support.md rename to docs/08_cuda_support.md From f03b345b485143b6da2a5a1b7cd94bb6c24d33a0 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:53:34 -0300 Subject: [PATCH 33/64] Rename 08_cuda_support.md to 07_cuda_support.md --- docs/{08_cuda_support.md => 07_cuda_support.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{08_cuda_support.md => 07_cuda_support.md} (100%) diff --git a/docs/08_cuda_support.md b/docs/07_cuda_support.md similarity index 100% rename from docs/08_cuda_support.md rename to docs/07_cuda_support.md From 715007f557ae30124d5bf40e023e5cc06af22b8b Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 08:54:01 -0300 Subject: [PATCH 34/64] Rename 07_troubleshooting.md to 08_troubleshooting.md --- docs/{07_troubleshooting.md => 08_troubleshooting.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{07_troubleshooting.md => 08_troubleshooting.md} (100%) diff --git a/docs/07_troubleshooting.md b/docs/08_troubleshooting.md similarity index 100% rename from docs/07_troubleshooting.md rename to docs/08_troubleshooting.md From 9ad6c88a769e77c14f935a3d20c4b65245852328 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 09:01:35 -0300 Subject: [PATCH 35/64] Update index.md --- docs/index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 49ec0be..7b33f3d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,7 +10,9 @@ This documentation provides insight into the motivation, technology, and future 4. [Use Cases](04_use_cases.md) 5. [Visualization](05_visualization.md) 6. [Discrete Traversal](06_discrete_traversal.md) -7. [API Reference](api_reference.md) +7. [CUDA Support](07_cuda_support.md) +8. [Troubleshooting & FAQ](08_troubleshooting.md) +9. [API Reference](09_API_reference.md) + [CONTRIBUTING](CONTRIBUTING.md) From f0465527a7265b6ae7b1ce64915663b620ca86b3 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 09:03:15 -0300 Subject: [PATCH 36/64] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1a6d0d6..1f63448 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SpinStep - [Read the Docs]( +# SpinStep - [Read the Docs](https://github.com/VoxLeone/SpinStep/docs/index.md **SpinStep** is a proof-of-concept quaternion-driven traversal framework for trees and orientation-based data structures. From 1fded87b2d234848e40c8e54d9fd1b1370abe570 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 09:03:32 -0300 Subject: [PATCH 37/64] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1f63448..4621490 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SpinStep - [Read the Docs](https://github.com/VoxLeone/SpinStep/docs/index.md +# SpinStep - [Read the Docs](https://github.com/VoxLeone/SpinStep/docs/index.md) **SpinStep** is a proof-of-concept quaternion-driven traversal framework for trees and orientation-based data structures. From befa98c5d256670c118bb53a034de7d31349852f Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 09:06:30 -0300 Subject: [PATCH 38/64] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4621490..2a2df26 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SpinStep - [Read the Docs](https://github.com/VoxLeone/SpinStep/docs/index.md) +# SpinStep - [Read the Docs](https://github.com/VoxLeone/SpinStep/tree/dev/docs/index.md) **SpinStep** is a proof-of-concept quaternion-driven traversal framework for trees and orientation-based data structures. From 5e1cbb73cdc76f41d512912f4582373a0a58f99c Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 09:13:27 -0300 Subject: [PATCH 39/64] Create gpu_orientation_matching.py --- examples/gpu_orientation_matching.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 examples/gpu_orientation_matching.py diff --git a/examples/gpu_orientation_matching.py b/examples/gpu_orientation_matching.py new file mode 100644 index 0000000..0ddf2ba --- /dev/null +++ b/examples/gpu_orientation_matching.py @@ -0,0 +1 @@ +i From 2af674d835e704870e85debf65a898eaaeb8cae5 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 09:15:25 -0300 Subject: [PATCH 40/64] Update gpu_orientation_matching.py --- examples/gpu_orientation_matching.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/examples/gpu_orientation_matching.py b/examples/gpu_orientation_matching.py index 0ddf2ba..f7042d4 100644 --- a/examples/gpu_orientation_matching.py +++ b/examples/gpu_orientation_matching.py @@ -1 +1,13 @@ -i +import numpy as np +from spinstep.discrete import DiscreteOrientationSet +from spinstep.utils.quaternion_math import batch_quaternion_angle + +N = 100_000 +orientations = np.random.randn(N, 4) +orientation_set = DiscreteOrientationSet(orientations, use_cuda=True) + +query = np.array([[0, 0, 0, 1]]) +xp = orientation_set.xp +angles = batch_quaternion_angle(xp.array(query), orientation_set.orientations, xp) +close_inds = xp.where(angles[0] < 0.1)[0] +print(f"Found {close_inds.size} close orientations (on GPU)") From 3588bba6a47b8fb4a4ffa788e3cf294ebdf4aa4f Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 12:03:30 -0300 Subject: [PATCH 41/64] Update and rename test.py to test_spinstep.py --- tests/test.py | 3 -- tests/test_spinstep.py | 98 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 3 deletions(-) delete mode 100644 tests/test.py create mode 100644 tests/test_spinstep.py diff --git a/tests/test.py b/tests/test.py deleted file mode 100644 index 2e4eccf..0000000 --- a/tests/test.py +++ /dev/null @@ -1,3 +0,0 @@ -# test.py — MIT License -# Author: Eraldo Marques — Created: 2025-05-14 -# See LICENSE.txt for full terms. This header must be retained in redistributions. diff --git a/tests/test_spinstep.py b/tests/test_spinstep.py new file mode 100644 index 0000000..ee5b828 --- /dev/null +++ b/tests/test_spinstep.py @@ -0,0 +1,98 @@ +# test_spinstep.py — MIT License +# Author: Eraldo Marques — Created: 2025-05-14 +# See LICENSE.txt for full terms. This header must be retained in redistributions. + +import pytest +import numpy as np + +try: + import cupy as cp + HAS_CUPY = True +except ImportError: + HAS_CUPY = False + +from spinstep.discrete import DiscreteOrientationSet +# If you have continuous traversal classes, import them here +# from spinstep.continuous import QuaternionDepthIterator +from spinstep.node import Node + +@pytest.fixture +def simple_tree(): + return Node("root", [0, 0, 0, 1], [ + Node("A", [0.707, 0, 0, 0.707]), + Node("B", [0, 0.707, 0, 0.707]) + ]) + +def test_discrete_orientation_set_cpu(): + arr = np.eye(4) + dset = DiscreteOrientationSet(arr) + assert len(dset) == 4 + assert np.allclose(np.linalg.norm(dset.orientations, axis=1), 1) + +def test_discrete_orientation_set_gpu(): + if not HAS_CUPY: + pytest.skip("CuPy not installed") + arr = np.eye(4) + dset = DiscreteOrientationSet(arr, use_cuda=True) + assert len(dset) == 4 + norms = dset.xp.linalg.norm(dset.orientations, axis=1) + assert dset.xp.allclose(norms, 1) + +def test_query_within_angle_cpu(): + arr = np.array([ + [0, 0, 0, 1], + [0.707, 0, 0, 0.707], + [0, 0.707, 0, 0.707] + ]) + dset = DiscreteOrientationSet(arr) + inds = dset.query_within_angle([0, 0, 0, 1], angle=1.0) + assert 0 in inds + +def test_query_within_angle_gpu(): + if not HAS_CUPY: + pytest.skip("CuPy not installed") + arr = np.array([ + [0, 0, 0, 1], + [0.707, 0, 0, 0.707], + [0, 0.707, 0, 0.707] + ]) + dset = DiscreteOrientationSet(arr, use_cuda=True) + inds = dset.query_within_angle([0, 0, 0, 1], angle=1.0) + assert 0 in dset.xp.asnumpy(inds) + +def test_from_cube_and_icosahedron(): + dset_cube = DiscreteOrientationSet.from_cube() + dset_ico = DiscreteOrientationSet.from_icosahedron() + assert len(dset_cube) == 24 + assert len(dset_ico) == 60 + +def test_from_custom(): + arr = np.eye(4) + dset = DiscreteOrientationSet.from_custom(arr) + assert len(dset) == 4 + +def test_from_sphere_grid(): + dset = DiscreteOrientationSet.from_sphere_grid(10) + assert len(dset) == 10 + +def test_invalid_quaternion(): + arr = np.zeros((3, 4)) + with pytest.raises(ValueError): + DiscreteOrientationSet(arr) + +def test_as_numpy_gpu(): + if not HAS_CUPY: + pytest.skip("CuPy not installed") + arr = np.eye(4) + dset = DiscreteOrientationSet(arr, use_cuda=True) + arr2 = dset.as_numpy() + assert isinstance(arr2, np.ndarray) + assert arr2.shape == (4, 4) + +# Example for continuous traversal (if implemented) +# def test_continuous_traversal(simple_tree): +# # Replace with your actual traversal class and logic +# it = QuaternionDepthIterator(simple_tree, angle_threshold=0.2) +# names = [node.name for node in it] +# assert "root" in names and "A" in names and "B" in names + From 064af2954d5524a4f5ffe8835d3944534e73e747 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 12:34:35 -0300 Subject: [PATCH 42/64] Create ci.yml --- .github/workflows/ci.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1d5a5c2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,37 @@ +name: SpinStep CI + +on: + push: + branches: [main, dev, 'features/**'] + pull_request: + branches: [main, dev] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.8', '3.9', '3.10', '3.11'] + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install .[dev] + + - name: Lint with flake8 + run: | + pip install flake8 + flake8 spinstep --count --select=E9,F63,F7,F82 --show-source --statistics + flake8 spinstep --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + - name: Test with pytest + run: | + pip install pytest + pytest tests/ From 1fa5d7f9143844024626af45be5df1d057c8ce7d Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 13:10:07 -0300 Subject: [PATCH 43/64] Update pyproject.toml --- pyproject.toml | 63 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 10716d5..04cc2f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,19 +1,64 @@ +[build-system] +requires = ["setuptools>=61", "wheel"] +build-backend = "setuptools.build_meta" + [project] name = "spinstep" version = "0.1.0" -description = "A quaternion-based tree traversal framework for orientation-aware data structures." +description = "A Python toolkit for batch and tree-based orientation math (SO(3), quaternions, etc)." authors = [ - { name="Your Name", email="your@email.com" } + { name = "Eraldo B. Marques", email = "eraldo.bernardo@gmail.com" } ] -license = "MIT" +license = { file = "LICENSE" } readme = "README.md" requires-python = ">=3.8" - dependencies = [ - "numpy >= 1.22", - "scipy >= 1.8" + "numpy>=1.22", + "scipy>=1.8", + "scikit-learn>=1.0", + # Optional: If you want to recommend CuPy for CUDA users but not require it + # "cupy; extra == 'cuda'", ] -[build-system] -requires = ["setuptools>=61.0"] -build-backend = "setuptools.build_meta" +[project.optional-dependencies] +dev = [ + "pytest", + "pytest-cov", + "black", + "mypy", + "flake8", + "isort", + "pre-commit", + "wheel", + "build", + "twine", + # Add any other dev/test tools you use here +] +docs = [ + "mkdocs", + "mkdocs-material", + # Add other doc tools as needed +] +cuda = [ + "cupy", +] + +[tool.setuptools] +packages = ["spinstep"] + +[tool.setuptools.packages.find] +where = ["spinstep"] + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "-ra -q" +testpaths = [ + "tests", +] + +[tool.black] +line-length = 88 +target-version = ["py38", "py39", "py310"] + +[tool.isort] +profile = "black" From f9735b1b0fec507b8020630847b6b3c6c401db9e Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 14:12:06 -0300 Subject: [PATCH 44/64] Update pyproject.toml --- pyproject.toml | 63 ++++++++------------------------------------------ 1 file changed, 9 insertions(+), 54 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 04cc2f6..10716d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,64 +1,19 @@ -[build-system] -requires = ["setuptools>=61", "wheel"] -build-backend = "setuptools.build_meta" - [project] name = "spinstep" version = "0.1.0" -description = "A Python toolkit for batch and tree-based orientation math (SO(3), quaternions, etc)." +description = "A quaternion-based tree traversal framework for orientation-aware data structures." authors = [ - { name = "Eraldo B. Marques", email = "eraldo.bernardo@gmail.com" } + { name="Your Name", email="your@email.com" } ] -license = { file = "LICENSE" } +license = "MIT" readme = "README.md" requires-python = ">=3.8" -dependencies = [ - "numpy>=1.22", - "scipy>=1.8", - "scikit-learn>=1.0", - # Optional: If you want to recommend CuPy for CUDA users but not require it - # "cupy; extra == 'cuda'", -] -[project.optional-dependencies] -dev = [ - "pytest", - "pytest-cov", - "black", - "mypy", - "flake8", - "isort", - "pre-commit", - "wheel", - "build", - "twine", - # Add any other dev/test tools you use here -] -docs = [ - "mkdocs", - "mkdocs-material", - # Add other doc tools as needed -] -cuda = [ - "cupy", -] - -[tool.setuptools] -packages = ["spinstep"] - -[tool.setuptools.packages.find] -where = ["spinstep"] - -[tool.pytest.ini_options] -minversion = "6.0" -addopts = "-ra -q" -testpaths = [ - "tests", +dependencies = [ + "numpy >= 1.22", + "scipy >= 1.8" ] -[tool.black] -line-length = 88 -target-version = ["py38", "py39", "py310"] - -[tool.isort] -profile = "black" +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" From 5f2c877dbf0af372b919c8b2bbb8e761c8546bdc Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 14:13:23 -0300 Subject: [PATCH 45/64] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 10716d5..9226e2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "spinstep" version = "0.1.0" description = "A quaternion-based tree traversal framework for orientation-aware data structures." authors = [ - { name="Your Name", email="your@email.com" } + { name="Eraldo Marques", email="eraldo.bernardoyour@gmail.com" } ] license = "MIT" readme = "README.md" From d2b73254da1541f3dbcc63bf1b8e55fb5076e764 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 14:13:48 -0300 Subject: [PATCH 46/64] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9226e2d..5edf02a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "spinstep" version = "0.1.0" description = "A quaternion-based tree traversal framework for orientation-aware data structures." authors = [ - { name="Eraldo Marques", email="eraldo.bernardoyour@gmail.com" } + { name="Eraldo Marques", email="eraldo.bernardo@gmail.com" } ] license = "MIT" readme = "README.md" From b098fa0ba2c7b2170cfd39e11903b30935330e8b Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 14:32:14 -0300 Subject: [PATCH 47/64] Update pyproject.toml --- pyproject.toml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5edf02a..d05ee1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,15 +3,21 @@ name = "spinstep" version = "0.1.0" description = "A quaternion-based tree traversal framework for orientation-aware data structures." authors = [ - { name="Eraldo Marques", email="eraldo.bernardo@gmail.com" } + { name = "Your Name", email = "your@email.com" } ] license = "MIT" readme = "README.md" requires-python = ">=3.8" dependencies = [ - "numpy >= 1.22", - "scipy >= 1.8" + "numpy>=1.22", + "scipy>=1.8" +] + +[project.optional-dependencies] +dev = [ + "pytest", + "flake8" ] [build-system] From ec2d77566a832aa43d62fa5d1e7856b917a5a2fd Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 15:00:44 -0300 Subject: [PATCH 48/64] Delete pyproject.toml --- pyproject.toml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 10716d5..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,19 +0,0 @@ -[project] -name = "spinstep" -version = "0.1.0" -description = "A quaternion-based tree traversal framework for orientation-aware data structures." -authors = [ - { name="Your Name", email="your@email.com" } -] -license = "MIT" -readme = "README.md" -requires-python = ">=3.8" - -dependencies = [ - "numpy >= 1.22", - "scipy >= 1.8" -] - -[build-system] -requires = ["setuptools>=61.0"] -build-backend = "setuptools.build_meta" From 2caad9c9bbcfc8613697a7cc43c192ea33cc0380 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 15:40:08 -0300 Subject: [PATCH 49/64] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d05ee1d..9f45ed8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "spinstep" version = "0.1.0" description = "A quaternion-based tree traversal framework for orientation-aware data structures." authors = [ - { name = "Your Name", email = "your@email.com" } + { name = "Eraldo Marques", email = "eraldo.bernardo@gmail.com" } ] license = "MIT" readme = "README.md" From 8008ae8fe54746c76088b533b83e804a8c0c7843 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 16:12:32 -0300 Subject: [PATCH 50/64] Update pyproject.toml --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9f45ed8..a3f9033 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,9 +3,9 @@ name = "spinstep" version = "0.1.0" description = "A quaternion-based tree traversal framework for orientation-aware data structures." authors = [ - { name = "Eraldo Marques", email = "eraldo.bernardo@gmail.com" } + { name = "Your Name", email = "your@email.com" } ] -license = "MIT" +license = { text = "MIT" } # Use this format for clarity readme = "README.md" requires-python = ">=3.8" From 170961286365860a69f2a1c812d347a1caa3dc0f Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 16:17:52 -0300 Subject: [PATCH 51/64] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a3f9033..635af27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "spinstep" version = "0.1.0" description = "A quaternion-based tree traversal framework for orientation-aware data structures." authors = [ - { name = "Your Name", email = "your@email.com" } + { name = "Eraldo Marques", email = "eraldo.bernardo@gmail.com" } ] license = { text = "MIT" } # Use this format for clarity readme = "README.md" From 74a19adc43d65f4b34ffdcba5a3a22b07c741447 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 16:27:31 -0300 Subject: [PATCH 52/64] Update pyproject.toml --- pyproject.toml | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 635af27..d0e5385 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,25 +1,42 @@ +[build-system] +requires = ["setuptools>=42", "wheel"] +build-backend = "setuptools.build_meta" + [project] -name = "spinstep" -version = "0.1.0" -description = "A quaternion-based tree traversal framework for orientation-aware data structures." +name = "spinStep" +version = "0.1.0" # You can adjust this version +description = "SpinStep: Computational library with CUDA support" authors = [ - { name = "Eraldo Marques", email = "eraldo.bernardo@gmail.com" } + {name = "Eraldo Marques", email = "eraldo.bernardo@gmail.com} ] -license = { text = "MIT" } # Use this format for clarity readme = "README.md" requires-python = ">=3.8" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] +# This section assumes your existing package structure +# You can adjust dependencies based on your actual requirements dependencies = [ - "numpy>=1.22", - "scipy>=1.8" + "numpy", + "scipy", ] [project.optional-dependencies] dev = [ "pytest", - "flake8" + "pytest-cov", +] +cuda = [ + "cupy-cuda11x", # Adjust CUDA version as needed ] -[build-system] -requires = ["setuptools>=61.0"] -build-backend = "setuptools.build_meta" +[tool.setuptools] +# Use automatic discovery to avoid structure changes +packages = "find:" + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = "test_*.py" From bfa638a80001ce1067ead4c0c875bfdb4eacaf72 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 16:30:50 -0300 Subject: [PATCH 53/64] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d0e5385..0ce9582 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "spinStep" version = "0.1.0" # You can adjust this version description = "SpinStep: Computational library with CUDA support" authors = [ - {name = "Eraldo Marques", email = "eraldo.bernardo@gmail.com} + {name = "Eraldo Marques", email = "eraldo.bernardo@gmail.com"} ] readme = "README.md" requires-python = ">=3.8" From fd0c0385401150c72d5dc741043b5d8de0db7f6d Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 16:37:41 -0300 Subject: [PATCH 54/64] Update pyproject.toml From b5dd4f4941c976b478124d03cc9ba12b564057f1 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 16:40:27 -0300 Subject: [PATCH 55/64] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d5a5c2..9292384 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: SpinStep CI on: push: - branches: [main, dev, 'features/**'] + branches: [main, dev, 'features/cuda'] pull_request: branches: [main, dev] From b7b0c9e30f9c2c1b106a642b1b63e023d94ce5aa Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 17:24:06 -0300 Subject: [PATCH 56/64] Update pyproject.toml --- pyproject.toml | 35 ++++------------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0ce9582..f40b39d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,40 +3,13 @@ requires = ["setuptools>=42", "wheel"] build-backend = "setuptools.build_meta" [project] -name = "spinStep" -version = "0.1.0" # You can adjust this version -description = "SpinStep: Computational library with CUDA support" +name = "SpinStep" +version = "0.1.0" +description = "A quaternion-driven traversal framework for trees and orientation-based data structures" authors = [ {name = "Eraldo Marques", email = "eraldo.bernardo@gmail.com"} ] -readme = "README.md" requires-python = ">=3.8" -classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", -] - -# This section assumes your existing package structure -# You can adjust dependencies based on your actual requirements -dependencies = [ - "numpy", - "scipy", -] - -[project.optional-dependencies] -dev = [ - "pytest", - "pytest-cov", -] -cuda = [ - "cupy-cuda11x", # Adjust CUDA version as needed -] [tool.setuptools] -# Use automatic discovery to avoid structure changes -packages = "find:" - -[tool.pytest.ini_options] -testpaths = ["tests"] -python_files = "test_*.py" +packages = ["spinStep"] From f24d79512d7d84706180eb3d95cd3e7ef61e4c2d Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 17:31:43 -0300 Subject: [PATCH 57/64] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f40b39d..82c609a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,4 +12,4 @@ authors = [ requires-python = ">=3.8" [tool.setuptools] -packages = ["spinStep"] +packages = ["SpinStep"] From 81718f760be0661b00c097a68a4e4a77db3247cd Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 17:48:44 -0300 Subject: [PATCH 58/64] Update pyproject.toml --- pyproject.toml | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 82c609a..0d9070b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,15 +1,25 @@ -[build-system] -requires = ["setuptools>=42", "wheel"] -build-backend = "setuptools.build_meta" - [project] name = "SpinStep" version = "0.1.0" -description = "A quaternion-driven traversal framework for trees and orientation-based data structures" +description = "A quaternion-based tree traversal framework for orientation-aware data structures." authors = [ - {name = "Eraldo Marques", email = "eraldo.bernardo@gmail.com"} + { name = "Eraldo Marques", email = "eraldo.bernardo@gmail.com" } ] +license = { text = "MIT" } # Use this format for clarity +readme = "README.md" requires-python = ">=3.8" -[tool.setuptools] -packages = ["SpinStep"] +dependencies = [ + "numpy>=1.22", + "scipy>=1.8" +] + +[project.optional-dependencies] +dev = [ + "pytest", + "flake8" +] + +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" From 46ab34d28fc342e6b79ff669e94885b221342deb Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 18:02:12 -0300 Subject: [PATCH 59/64] Update pyproject.toml --- pyproject.toml | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0d9070b..1b889ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,25 +1,16 @@ +[build-system] +requires = ["setuptools>=42", "wheel"] +build-backend = "setuptools.build_meta" + +# Empty project table to avoid metadata conflicts [project] name = "SpinStep" version = "0.1.0" -description = "A quaternion-based tree traversal framework for orientation-aware data structures." -authors = [ - { name = "Eraldo Marques", email = "eraldo.bernardo@gmail.com" } -] -license = { text = "MIT" } # Use this format for clarity -readme = "README.md" +description = "Quaternion-driven traversal framework" requires-python = ">=3.8" +# Mark everything as dynamic to let setup.py handle it +dynamic = ["dependencies", "readme", "license", "authors"] -dependencies = [ - "numpy>=1.22", - "scipy>=1.8" -] - -[project.optional-dependencies] -dev = [ - "pytest", - "flake8" -] - -[build-system] -requires = ["setuptools>=61.0"] -build-backend = "setuptools.build_meta" +# Skip package discovery completely - just use what's in setup.py +[tool.setuptools.dynamic] +version = {attr = "spinStep.__version__"} From 6f47bb89860633827b6c1a72e87d1ea4b20c6c88 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 18:07:26 -0300 Subject: [PATCH 60/64] Update pyproject.toml --- pyproject.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1b889ea..8c2d4eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,4 @@ -[build-system] -requires = ["setuptools>=42", "wheel"] -build-backend = "setuptools.build_meta" +uild-backend = "setuptools.build_meta" # Empty project table to avoid metadata conflicts [project] @@ -13,4 +11,4 @@ dynamic = ["dependencies", "readme", "license", "authors"] # Skip package discovery completely - just use what's in setup.py [tool.setuptools.dynamic] -version = {attr = "spinStep.__version__"} +version = {attr = "SpinStep.__version__"} From b2c083c08720bc46b04613fc28b6ec22ed020678 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 18:33:41 -0300 Subject: [PATCH 61/64] Update pyproject.toml --- pyproject.toml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8c2d4eb..3de8b0e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,14 @@ -uild-backend = "setuptools.build_meta" +[build-system] +requires = ["setuptools>=42", "wheel"] +build-backend = "setuptools.build_meta" -# Empty project table to avoid metadata conflicts +# Most minimal configuration possible [project] name = "SpinStep" -version = "0.1.0" -description = "Quaternion-driven traversal framework" +version = "0.0.0" # Placeholder version +description = "Work in progress" requires-python = ">=3.8" -# Mark everything as dynamic to let setup.py handle it -dynamic = ["dependencies", "readme", "license", "authors"] -# Skip package discovery completely - just use what's in setup.py -[tool.setuptools.dynamic] -version = {attr = "SpinStep.__version__"} +# Skip package discovery completely +[tool.setuptools] +py-modules = [] # Empty list - don't try to find any packages From 51e1cf3236cbfec050e099b681f46569f5129782 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Thu, 15 May 2025 19:48:38 -0300 Subject: [PATCH 62/64] Delete pyproject.toml --- pyproject.toml | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 3de8b0e..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,14 +0,0 @@ -[build-system] -requires = ["setuptools>=42", "wheel"] -build-backend = "setuptools.build_meta" - -# Most minimal configuration possible -[project] -name = "SpinStep" -version = "0.0.0" # Placeholder version -description = "Work in progress" -requires-python = ">=3.8" - -# Skip package discovery completely -[tool.setuptools] -py-modules = [] # Empty list - don't try to find any packages From f58e1b95680ebeff76235c7b3fe2761dc3dc552f Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Fri, 16 May 2025 09:08:38 -0300 Subject: [PATCH 63/64] Add files via upload --- tests/test_discrete_traversal_Version2.py | 272 ++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 tests/test_discrete_traversal_Version2.py diff --git a/tests/test_discrete_traversal_Version2.py b/tests/test_discrete_traversal_Version2.py new file mode 100644 index 0000000..4cf3b39 --- /dev/null +++ b/tests/test_discrete_traversal_Version2.py @@ -0,0 +1,272 @@ +# test_discrete_traversal.py — SpinStep Test Suite — MIT License +# Author: Eraldo B. Marques — Created: 2025-05-16 +# See LICENSE.txt for full terms. + +import numpy as np +import pytest +from scipy.spatial.transform import Rotation as R + +# Import the modules under test +from spinstep.orientations.discrete import DiscreteOrientationSet +from spinstep.traversal.discrete_iterator import DiscreteQuaternionIterator + +# Simple node class for testing +class Node: + def __init__(self, id, orientation, children=None): + self.id = id + self.orientation = orientation # [x,y,z,w] quaternion + self.children = children or [] + + def add_child(self, child): + self.children.append(child) + return child + + def __repr__(self): + return f"Node({self.id})" + + +# ===== DiscreteOrientationSet Tests ===== + +class TestDiscreteOrientationSet: + def test_initialization(self): + # Test with valid quaternions + quats = [ + [0, 0, 0, 1], # Identity + [0, 1, 0, 0], # 180° around Y + [0, 0, 1, 0], # 180° around Z + ] + orientation_set = DiscreteOrientationSet(quats) + assert len(orientation_set) == 3 + + # Test normalization + unnormalized = [ + [0, 0, 0, 2], # Non-unit quaternion + ] + orientation_set = DiscreteOrientationSet(unnormalized) + assert np.allclose(orientation_set.orientations[0], [0, 0, 0, 1]) + + # Test error cases + with pytest.raises(ValueError): + DiscreteOrientationSet([[0, 0, 0]]) # Wrong shape + + with pytest.raises(ValueError): + DiscreteOrientationSet([[0, 0, 0, 0]]) # Zero quaternion + + def test_predefined_sets(self): + # Test cube (octahedral) set - should have 24 orientations + cube_set = DiscreteOrientationSet.from_cube() + assert len(cube_set) == 24 + + # Test icosahedral set - should have 60 orientations + icosa_set = DiscreteOrientationSet.from_icosahedron() + assert len(icosa_set) == 60 + + # Test custom set + custom_quats = [[0, 0, 0, 1], [1, 0, 0, 0]] + custom_set = DiscreteOrientationSet.from_custom(custom_quats) + assert len(custom_set) == 2 + + # Test sphere grid with different point counts + sphere_set_small = DiscreteOrientationSet.from_sphere_grid(n_points=10) + assert len(sphere_set_small) == 10 + + sphere_set_large = DiscreteOrientationSet.from_sphere_grid(n_points=100) + assert len(sphere_set_large) == 100 + + def test_query_within_angle(self): + # Create a set with known angles + quats = [ + [0, 0, 0, 1], # Identity + [0, np.sin(np.pi/8), 0, np.cos(np.pi/8)], # 45° around Y + [0, np.sin(np.pi/4), 0, np.cos(np.pi/4)], # 90° around Y + ] + orientation_set = DiscreteOrientationSet(quats) + + # Query within small angle - should only find identity + results = orientation_set.query_within_angle([0, 0, 0, 1], np.pi/16) + assert len(results) == 1 + + # Query within medium angle - should find identity and 45° + results = orientation_set.query_within_angle([0, 0, 0, 1], np.pi/6) + assert len(results) == 2 + + # Query within large angle - should find all three + results = orientation_set.query_within_angle([0, 0, 0, 1], np.pi/3) + assert len(results) == 3 + + @pytest.mark.skipif(not np.array([True]), reason="CUDA not available") + def test_cuda_support(self): + try: + cuda_set = DiscreteOrientationSet([[0, 0, 0, 1]], use_cuda=True) + # If this succeeds, basic CUDA import worked + assert cuda_set.use_cuda == True + + # Test as_numpy() method for GPU->CPU transfer + cpu_array = cuda_set.as_numpy() + assert isinstance(cpu_array, np.ndarray) + except (ImportError, ModuleNotFoundError): + pytest.skip("CUDA/CuPy not available") + + +# ===== DiscreteQuaternionIterator Tests ===== + +class TestDiscreteQuaternionIterator: + def setup_method(self): + # Create a simple tree structure with specific orientations + # root (identity orientation) + # / \ + # A B + # / \ / \ + # C D E F + # + # Where A is 45° rotation from root around Y + # B is 90° rotation from root around Y + # Others have further rotations + + self.root = Node("root", [0, 0, 0, 1]) + + # 45° around Y + self.node_a = self.root.add_child( + Node("A", [0, np.sin(np.pi/8), 0, np.cos(np.pi/8)]) + ) + + # 90° around Y + self.node_b = self.root.add_child( + Node("B", [0, np.sin(np.pi/4), 0, np.cos(np.pi/4)]) + ) + + # A's children - small variations + self.node_c = self.node_a.add_child( + Node("C", [0, np.sin(np.pi/8+0.1), 0, np.cos(np.pi/8+0.1)]) + ) + + self.node_d = self.node_a.add_child( + Node("D", [0, np.sin(np.pi/8-0.1), 0, np.cos(np.pi/8-0.1)]) + ) + + # B's children - small variations + self.node_e = self.node_b.add_child( + Node("E", [0, np.sin(np.pi/4+0.1), 0, np.cos(np.pi/4+0.1)]) + ) + + self.node_f = self.node_b.add_child( + Node("F", [0, np.sin(np.pi/4-0.1), 0, np.cos(np.pi/4-0.1)]) + ) + + # Create orientation set with basic steps + steps = [ + [0, 0, 0, 1], # Identity (no rotation) + [0, np.sin(np.pi/8), 0, np.cos(np.pi/8)], # 45° step around Y + [0, np.sin(-np.pi/8), 0, np.cos(np.pi/8)], # -45° step around Y + ] + self.orientation_set = DiscreteOrientationSet(steps) + + def test_iterator_creation(self): + # Test basic creation + iterator = DiscreteQuaternionIterator( + self.root, + self.orientation_set, + angle_threshold=np.pi/6 + ) + assert iterator is not None + + # Test error with invalid node + class InvalidNode: + pass + + with pytest.raises(AttributeError): + DiscreteQuaternionIterator( + InvalidNode(), + self.orientation_set + ) + + def test_traversal(self): + # With large angle threshold, should visit all nodes + iterator = DiscreteQuaternionIterator( + self.root, + self.orientation_set, + angle_threshold=np.pi/2, # Very permissive + max_depth=3 + ) + + visited_nodes = list(iterator) + visited_ids = [node.id for node in visited_nodes] + + # Should have visited all nodes in some order + for node_id in ["root", "A", "B", "C", "D", "E", "F"]: + assert node_id in visited_ids + + # With tiny threshold, should only visit root + iterator = DiscreteQuaternionIterator( + self.root, + self.orientation_set, + angle_threshold=np.pi/64, # Very restrictive + max_depth=3 + ) + + visited_nodes = list(iterator) + assert len(visited_nodes) == 1 + assert visited_nodes[0].id == "root" + + def test_max_depth(self): + # With max_depth=1, should only visit root and its direct children + iterator = DiscreteQuaternionIterator( + self.root, + self.orientation_set, + angle_threshold=np.pi/2, # Very permissive + max_depth=1 + ) + + visited_nodes = list(iterator) + visited_ids = [node.id for node in visited_nodes] + + # Should have visited only root and direct children + for node_id in ["root", "A", "B"]: + assert node_id in visited_ids + + # Shouldn't have visited grandchildren + for node_id in ["C", "D", "E", "F"]: + assert node_id not in visited_ids + + +# ===== Integration Tests ===== + +def test_full_pipeline(): + """Test the complete pipeline from orientation set creation to traversal""" + + # Create a custom orientation set + custom_steps = [ + [0, 0, 0, 1], # Identity + [1, 0, 0, 0], # 180° around X + [0, 1, 0, 0], # 180° around Y + [0, 0, 1, 0], # 180° around Z + ] + + orientation_set = DiscreteOrientationSet.from_custom(custom_steps) + + # Create a simple tree + root = Node("root", [0, 0, 0, 1]) + node_a = root.add_child(Node("A", [1, 0, 0, 0])) + node_b = root.add_child(Node("B", [0, 1, 0, 0])) + + # Create iterator and traverse + iterator = DiscreteQuaternionIterator( + root, + orientation_set, + angle_threshold=np.pi/4 + ) + + # Check that we can iterate and get nodes + visited = [] + for node in iterator: + visited.append(node.id) + + # We expect to visit all nodes since our steps include direct rotations + # to each child's orientation + assert "root" in visited + assert "A" in visited + assert "B" in visited + + +if __name__ == "__main__": + pytest.main(["-xvs", __file__]) \ No newline at end of file From 74d43bf948f76f6d3d02ca7b9ede23e36806e091 Mon Sep 17 00:00:00 2001 From: Eraldo Marques <119956342+VoxLeone@users.noreply.github.com> Date: Fri, 16 May 2025 09:09:37 -0300 Subject: [PATCH 64/64] Rename test_discrete_traversal_Version2.py to test_discrete_traversal.py --- ...iscrete_traversal_Version2.py => test_discrete_traversal.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/{test_discrete_traversal_Version2.py => test_discrete_traversal.py} (99%) diff --git a/tests/test_discrete_traversal_Version2.py b/tests/test_discrete_traversal.py similarity index 99% rename from tests/test_discrete_traversal_Version2.py rename to tests/test_discrete_traversal.py index 4cf3b39..9d0a77c 100644 --- a/tests/test_discrete_traversal_Version2.py +++ b/tests/test_discrete_traversal.py @@ -269,4 +269,4 @@ def test_full_pipeline(): if __name__ == "__main__": - pytest.main(["-xvs", __file__]) \ No newline at end of file + pytest.main(["-xvs", __file__])