spectrochempy-hypercomplex 0.1.1__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spectrochempy-hypercomplex
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Hypercomplex / quaternion support plugin for SpectroChemPy
5
5
  Requires-Python: >=3.11
6
- Requires-Dist: spectrochempy<0.10,>=0.9.0.dev0
6
+ Requires-Dist: spectrochempy<0.10,>=0.9
7
7
  Requires-Dist: numpy
8
8
  Requires-Dist: numpy-quaternion>=2024.0.7
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "spectrochempy-hypercomplex"
7
- version = "0.1.1"
7
+ version = "0.1.2"
8
8
  description = "Hypercomplex / quaternion support plugin for SpectroChemPy"
9
9
  requires-python = ">=3.11"
10
10
  dependencies = [
11
- "spectrochempy>=0.9.0.dev0,<0.10",
11
+ "spectrochempy>=0.9,<0.10",
12
12
  "numpy",
13
13
  "numpy-quaternion>=2024.0.7",
14
14
  ]
@@ -310,9 +310,9 @@ class HyperComplexPlugin(SpectroChemPyPlugin):
310
310
  """Hypercomplex / quaternion support for SpectroChemPy."""
311
311
 
312
312
  name = "hypercomplex"
313
- version = "0.1.0"
313
+ version = "0.1.2"
314
314
  description = "Hypercomplex / quaternion data support"
315
- spectrochempy_min_version = "0.9.0.dev0"
315
+ spectrochempy_min_version = "0.9.0"
316
316
  PLUGIN_API_VERSION = CORE_PLUGIN_API_VERSION
317
317
  capabilities = [PluginCapability.ACCESSOR]
318
318
 
@@ -65,10 +65,8 @@ def as_quaternion(*args):
65
65
  else:
66
66
  raise ValueError("as_quaternion requires 2 or 4 arguments")
67
67
 
68
- data = as_quat_array(
69
- list(zip(w.flatten(), x.flatten(), y.flatten(), z.flatten(), strict=False)),
70
- )
71
- return data.reshape(w.shape)
68
+ stacked = np.stack([w, x, y, z], axis=-1)
69
+ return as_quat_array(stacked)
72
70
 
73
71
 
74
72
  def quat_as_complex_array(arr):
@@ -90,8 +88,11 @@ def quat_as_complex_array(arr):
90
88
  if typequaternion is None or arr.dtype != typequaternion:
91
89
  return arr
92
90
 
93
- wt, xt, yt, zt = as_float_array(arr).T
94
- w, x, y, z = wt.T, xt.T, yt.T, zt.T
91
+ float_arr = as_float_array(arr)
92
+ w = float_arr[..., 0]
93
+ x = float_arr[..., 1]
94
+ y = float_arr[..., 2]
95
+ z = float_arr[..., 3]
95
96
 
96
97
  return (w + 1j * x), (y + 1j * z)
97
98
 
@@ -136,8 +137,11 @@ def get_component(data, select="REAL"):
136
137
  w = x = y = z = None
137
138
 
138
139
  if typequaternion is not None and new.dtype == typequaternion:
139
- w, x, y, z = as_float_array(new).T
140
- w, x, y, z = w.T, x.T, y.T, z.T
140
+ float_arr = as_float_array(new)
141
+ w = float_arr[..., 0]
142
+ x = float_arr[..., 1]
143
+ y = float_arr[..., 2]
144
+ z = float_arr[..., 3]
141
145
  if select == "R":
142
146
  new = w + x * 1j
143
147
  elif select == "I":
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spectrochempy-hypercomplex
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Hypercomplex / quaternion support plugin for SpectroChemPy
5
5
  Requires-Python: >=3.11
6
- Requires-Dist: spectrochempy<0.10,>=0.9.0.dev0
6
+ Requires-Dist: spectrochempy<0.10,>=0.9
7
7
  Requires-Dist: numpy
8
8
  Requires-Dist: numpy-quaternion>=2024.0.7
@@ -1,3 +1,3 @@
1
- spectrochempy<0.10,>=0.9.0.dev0
1
+ spectrochempy<0.10,>=0.9
2
2
  numpy
3
3
  numpy-quaternion>=2024.0.7
@@ -0,0 +1,126 @@
1
+ # ruff: noqa: S101
2
+ import numpy as np
3
+ import pytest
4
+
5
+
6
+ class TestHyperAccessor:
7
+ def test_accessor_registered(self):
8
+ import spectrochempy as scp
9
+
10
+ ds = scp.NDDataset([1.0, 2.0, 3.0])
11
+ assert hasattr(ds, "hyper")
12
+
13
+ def test_is_quaternion_false_for_real(self):
14
+ import spectrochempy as scp
15
+
16
+ ds = scp.NDDataset([1.0, 2.0, 3.0])
17
+ assert ds.hyper.is_quaternion is False
18
+
19
+ def test_set_quaternion_creates_quaternion_dtype(self):
20
+ import spectrochempy as scp
21
+
22
+ ds = scp.NDDataset([1.0, 2.0, 3.0, 4.0])
23
+ result = ds.hyper.set_quaternion()
24
+ assert result._data.dtype.name == "quaternion"
25
+
26
+ def test_component_real_data_noop(self):
27
+ import spectrochempy as scp
28
+
29
+ ds = scp.NDDataset([1.0, 2.0, 3.0])
30
+ result = ds.hyper.component("RR")
31
+ assert result is not None
32
+
33
+ def test_RR_RI_IR_II_raise_for_real(self):
34
+ import spectrochempy as scp
35
+
36
+ ds = scp.NDDataset([1.0, 2.0, 3.0])
37
+ for prop in ["RR", "RI", "IR", "II"]:
38
+ with pytest.raises(TypeError):
39
+ getattr(ds.hyper, prop)
40
+
41
+
42
+ class TestNDMathHandlers:
43
+ def test_handler_registration(self):
44
+ from spectrochempy.plugins import manager
45
+
46
+ registry = manager.plugin_manager.registry
47
+ assert registry.get_handler("ndmath.execution_branch") is not None
48
+ assert registry.get_handler("ndmath.execute") is not None
49
+
50
+
51
+ class TestQuaternionFunctions:
52
+ def test_as_quaternion_roundtrip(self):
53
+ from spectrochempy_hypercomplex._quaternion import as_quaternion
54
+ from spectrochempy_hypercomplex._quaternion import quat_as_complex_array
55
+
56
+ w = np.array([1.0, 2.0, 3.0])
57
+ x = np.array([0.5, 0.6, 0.7])
58
+ q = as_quaternion(w, x, w, x)
59
+ c1, c2 = quat_as_complex_array(q)
60
+ assert np.allclose(c1, w + 1j * x)
61
+ assert np.allclose(c2, w + 1j * x)
62
+
63
+ def test_as_quaternion_2args(self):
64
+ from spectrochempy_hypercomplex._quaternion import as_quaternion
65
+
66
+ r = np.array([1.0, 2.0]) + 1j * np.array([0.5, 0.6])
67
+ i = np.array([3.0, 4.0]) + 1j * np.array([0.7, 0.8])
68
+ q = as_quaternion(r, i)
69
+ assert q.dtype.name == "quaternion"
70
+ assert q.shape == (2,)
71
+
72
+ def test_as_quaternion_2d_shape_preserved(self):
73
+ from spectrochempy_hypercomplex._quaternion import as_quaternion
74
+
75
+ w = np.ones((3, 4))
76
+ x = np.zeros((3, 4))
77
+ q = as_quaternion(w, x, w, x)
78
+ assert q.shape == (3, 4)
79
+
80
+ def test_quat_as_complex_array_nd(self):
81
+ from spectrochempy_hypercomplex._quaternion import as_quaternion
82
+ from spectrochempy_hypercomplex._quaternion import quat_as_complex_array
83
+
84
+ w = np.arange(24.0).reshape(2, 3, 4)
85
+ x = np.arange(24.0, 48.0).reshape(2, 3, 4)
86
+ q = as_quaternion(w, x, w, x)
87
+ c1, c2 = quat_as_complex_array(q)
88
+ assert c1.shape == (2, 3, 4)
89
+ assert np.allclose(c1, w + 1j * x)
90
+
91
+ def test_get_component_quaternion_2d(self):
92
+ from spectrochempy_hypercomplex._quaternion import as_quaternion
93
+ from spectrochempy_hypercomplex._quaternion import get_component
94
+
95
+ w = np.ones((3, 4))
96
+ x = np.full((3, 4), 2.0)
97
+ y = np.full((3, 4), 3.0)
98
+ z = np.full((3, 4), 4.0)
99
+ q = as_quaternion(w, x, y, z)
100
+ assert np.allclose(get_component(q, "RR"), w)
101
+ assert np.allclose(get_component(q, "RI"), x)
102
+ assert np.allclose(get_component(q, "IR"), y)
103
+ assert np.allclose(get_component(q, "II"), z)
104
+
105
+ def test_get_component_raises_bad_select(self):
106
+ from spectrochempy_hypercomplex._quaternion import as_quaternion
107
+ from spectrochempy_hypercomplex._quaternion import get_component
108
+
109
+ q = as_quaternion(np.ones(3), np.zeros(3), np.ones(3), np.zeros(3))
110
+ with pytest.raises(ValueError, match="cannot interpret"):
111
+ get_component(q, "RRI")
112
+
113
+ def test_data_remains_real_noop(self):
114
+ from spectrochempy_hypercomplex._quaternion import get_component
115
+
116
+ arr = np.array([1.0, 2.0, 3.0])
117
+ result = get_component(arr, "RR")
118
+ assert np.array_equal(result, arr)
119
+
120
+ def test_interleaved2complex(self):
121
+ from spectrochempy_hypercomplex._quaternion import interleaved2complex
122
+
123
+ data = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
124
+ result = interleaved2complex(data)
125
+ expected = np.array([1.0 + 2.0j, 3.0 + 4.0j, 5.0 + 6.0j])
126
+ assert np.allclose(result, expected)
@@ -1,49 +0,0 @@
1
- # ruff: noqa: S101
2
- import pytest
3
-
4
-
5
- class TestHyperAccessor:
6
- def test_accessor_registered(self):
7
- import spectrochempy as scp
8
-
9
- ds = scp.NDDataset([1.0, 2.0, 3.0])
10
- assert hasattr(ds, "hyper")
11
-
12
- def test_is_quaternion_false_for_real(self):
13
- import spectrochempy as scp
14
-
15
- ds = scp.NDDataset([1.0, 2.0, 3.0])
16
- assert ds.hyper.is_quaternion is False
17
-
18
- def test_set_quaternion_creates_quaternion_dtype(self):
19
- import spectrochempy as scp
20
-
21
- ds = scp.NDDataset([1.0, 2.0, 3.0, 4.0])
22
- result = ds.hyper.set_quaternion()
23
- # After conversion, the data should have a quaternion dtype
24
- assert result._data.dtype.name == "quaternion"
25
-
26
- def test_component_real_data_noop(self):
27
- import spectrochempy as scp
28
-
29
- ds = scp.NDDataset([1.0, 2.0, 3.0])
30
- # For real data, get_component returns the array unchanged
31
- result = ds.hyper.component("RR")
32
- assert result is not None
33
-
34
- def test_RR_RI_IR_II_raise_for_real(self):
35
- import spectrochempy as scp
36
-
37
- ds = scp.NDDataset([1.0, 2.0, 3.0])
38
- for prop in ["RR", "RI", "IR", "II"]:
39
- with pytest.raises(TypeError):
40
- getattr(ds.hyper, prop)
41
-
42
-
43
- class TestNDMathHandlers:
44
- def test_handler_registration(self):
45
- from spectrochempy.plugins import manager
46
-
47
- registry = manager.plugin_manager.registry
48
- assert registry.get_handler("ndmath.execution_branch") is not None
49
- assert registry.get_handler("ndmath.execute") is not None