clif-ga 0.1.0__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.
- clif_ga-0.1.0/LICENSE +9 -0
- clif_ga-0.1.0/PKG-INFO +93 -0
- clif_ga-0.1.0/README.md +59 -0
- clif_ga-0.1.0/clif/__init__.py +92 -0
- clif_ga-0.1.0/clif/context.py +312 -0
- clif_ga-0.1.0/clif/inverse/__init__.py +26 -0
- clif_ga-0.1.0/clif/inverse/euclidean.py +60 -0
- clif_ga-0.1.0/clif/inverse/fls.py +174 -0
- clif_ga-0.1.0/clif/inverse/fls_eigen.py +213 -0
- clif_ga-0.1.0/clif/inverse/low_dim_inverse.py +61 -0
- clif_ga-0.1.0/clif/inverse/model_inverse.py +122 -0
- clif_ga-0.1.0/clif/inverse/newton.py +73 -0
- clif_ga-0.1.0/clif/inverse/sparse_fls.py +755 -0
- clif_ga-0.1.0/clif/inverse/sparse_fls_dd.py +557 -0
- clif_ga-0.1.0/clif/inverse/tau_api.py +107 -0
- clif_ga-0.1.0/clif/multivector.py +511 -0
- clif_ga-0.1.0/clif/sign_table.py +313 -0
- clif_ga-0.1.0/clif/tools.py +602 -0
- clif_ga-0.1.0/clif/util.py +667 -0
- clif_ga-0.1.0/clif_ga.egg-info/PKG-INFO +93 -0
- clif_ga-0.1.0/clif_ga.egg-info/SOURCES.txt +28 -0
- clif_ga-0.1.0/clif_ga.egg-info/dependency_links.txt +1 -0
- clif_ga-0.1.0/clif_ga.egg-info/requires.txt +13 -0
- clif_ga-0.1.0/clif_ga.egg-info/top_level.txt +1 -0
- clif_ga-0.1.0/pyproject.toml +41 -0
- clif_ga-0.1.0/setup.cfg +4 -0
- clif_ga-0.1.0/tests/test_inverse_modules.py +80 -0
- clif_ga-0.1.0/tests/test_model.py +104 -0
- clif_ga-0.1.0/tests/test_roundtrip.py +48 -0
- clif_ga-0.1.0/tests/test_sparse_fls.py +153 -0
clif_ga-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 D. A. Jones
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
clif_ga-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: clif-ga
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Clifford-algebra engine: multivector inverse algorithms
|
|
5
|
+
Author: D. A. Jones
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 D. A. Jones
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
15
|
+
|
|
16
|
+
Project-URL: Homepage, https://github.com/dajonesy/engine
|
|
17
|
+
Project-URL: Documentation, https://clif-ga.readthedocs.io
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: numpy>=1.24
|
|
22
|
+
Requires-Dist: numba>=0.58
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: jupyter; extra == "dev"
|
|
25
|
+
Requires-Dist: ipykernel; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff; extra == "dev"
|
|
27
|
+
Requires-Dist: myst-nb; extra == "dev"
|
|
28
|
+
Requires-Dist: jupyter-cache; extra == "dev"
|
|
29
|
+
Requires-Dist: matplotlib; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest; extra == "dev"
|
|
31
|
+
Requires-Dist: sphinx; extra == "dev"
|
|
32
|
+
Requires-Dist: furo; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# clif
|
|
36
|
+
|
|
37
|
+
A research toolkit for numerical computations in Clifford / geometric algebras,
|
|
38
|
+
with an emphasis on **fast multivector inverses**, including a 6D inverse and
|
|
39
|
+
an emerging dimension-independent approach.
|
|
40
|
+
|
|
41
|
+
> **Status:** research prototype, not yet a packaged release.
|
|
42
|
+
> The API is unstable and the test suite is still being built out.
|
|
43
|
+
> Star the repo if you want to follow along.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## What's here
|
|
48
|
+
|
|
49
|
+
The package implements Clifford algebra with explicit ordinal indexing of
|
|
50
|
+
multivector components, efficient sign-table-based multiplication, and support
|
|
51
|
+
for arbitrary metric signature and degeneracy.
|
|
52
|
+
It's organized as four cooperating layers:
|
|
53
|
+
|
|
54
|
+
- `clif.context` — global algebra state (dimension, signature, degeneracy, grade table).
|
|
55
|
+
- `clif.sign_table` — builds/caches the multiplication sign table and a Numba-jitted fast
|
|
56
|
+
multiplier for dimensions < 8.
|
|
57
|
+
- `clif.multivector` — the `Accum` class, a multivector as a NumPy array of real coefficients indexed by ordinal.
|
|
58
|
+
- `clif.util` — construction, printing, grade projection, involutions, and inverse models for specific dimensions.
|
|
59
|
+
- `clif.tools` — primitive signtable support functions
|
|
60
|
+
|
|
61
|
+
Inverse algorithms live in `clif.inverse`:
|
|
62
|
+
|
|
63
|
+
- `clif.inverse.fls` (and `clif.inverse.sparse_fls`) — Faddeev–LeVerrier–Souriau based inverses, with even/odd-graded variants.
|
|
64
|
+
- `clif.inverse.euclidean` — inverse for Euclidean (positive-definite) signatures.
|
|
65
|
+
- `clif.inverse.newton` — iterative refinement via Newton–Schulz.
|
|
66
|
+
|
|
67
|
+
## A worked example
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from clif.context import Cl, Layout
|
|
71
|
+
from clif.multivector import Accum
|
|
72
|
+
|
|
73
|
+
Cl(1, 1, 1) # algebra with 1 positive, 1 negative, 1 null basis vector
|
|
74
|
+
A = Accum.random()
|
|
75
|
+
print(A)
|
|
76
|
+
|
|
77
|
+
Cl(1, 3) # spacetime algebra Cl(1,3)
|
|
78
|
+
Layout([1, -1, -1, -1]) # equivalent, explicit
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Documentation
|
|
82
|
+
|
|
83
|
+
Built locally with Sphinx:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
cd docs && make html
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Then open `docs/_build/html/index.html` in a browser.
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
MIT. See [LICENSE](LICENSE).
|
clif_ga-0.1.0/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# clif
|
|
2
|
+
|
|
3
|
+
A research toolkit for numerical computations in Clifford / geometric algebras,
|
|
4
|
+
with an emphasis on **fast multivector inverses**, including a 6D inverse and
|
|
5
|
+
an emerging dimension-independent approach.
|
|
6
|
+
|
|
7
|
+
> **Status:** research prototype, not yet a packaged release.
|
|
8
|
+
> The API is unstable and the test suite is still being built out.
|
|
9
|
+
> Star the repo if you want to follow along.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What's here
|
|
14
|
+
|
|
15
|
+
The package implements Clifford algebra with explicit ordinal indexing of
|
|
16
|
+
multivector components, efficient sign-table-based multiplication, and support
|
|
17
|
+
for arbitrary metric signature and degeneracy.
|
|
18
|
+
It's organized as four cooperating layers:
|
|
19
|
+
|
|
20
|
+
- `clif.context` — global algebra state (dimension, signature, degeneracy, grade table).
|
|
21
|
+
- `clif.sign_table` — builds/caches the multiplication sign table and a Numba-jitted fast
|
|
22
|
+
multiplier for dimensions < 8.
|
|
23
|
+
- `clif.multivector` — the `Accum` class, a multivector as a NumPy array of real coefficients indexed by ordinal.
|
|
24
|
+
- `clif.util` — construction, printing, grade projection, involutions, and inverse models for specific dimensions.
|
|
25
|
+
- `clif.tools` — primitive signtable support functions
|
|
26
|
+
|
|
27
|
+
Inverse algorithms live in `clif.inverse`:
|
|
28
|
+
|
|
29
|
+
- `clif.inverse.fls` (and `clif.inverse.sparse_fls`) — Faddeev–LeVerrier–Souriau based inverses, with even/odd-graded variants.
|
|
30
|
+
- `clif.inverse.euclidean` — inverse for Euclidean (positive-definite) signatures.
|
|
31
|
+
- `clif.inverse.newton` — iterative refinement via Newton–Schulz.
|
|
32
|
+
|
|
33
|
+
## A worked example
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from clif.context import Cl, Layout
|
|
37
|
+
from clif.multivector import Accum
|
|
38
|
+
|
|
39
|
+
Cl(1, 1, 1) # algebra with 1 positive, 1 negative, 1 null basis vector
|
|
40
|
+
A = Accum.random()
|
|
41
|
+
print(A)
|
|
42
|
+
|
|
43
|
+
Cl(1, 3) # spacetime algebra Cl(1,3)
|
|
44
|
+
Layout([1, -1, -1, -1]) # equivalent, explicit
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Documentation
|
|
48
|
+
|
|
49
|
+
Built locally with Sphinx:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cd docs && make html
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Then open `docs/_build/html/index.html` in a browser.
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# file: clif/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
clif — a Python package for Clifford algebra.
|
|
4
|
+
|
|
5
|
+
This package implements Clifford algebra (geometric algebra) with a focus on
|
|
6
|
+
explicit ordinal indexing of multivector components, efficient sign-table-based
|
|
7
|
+
multiplication, and support for arbitrary metric signature and degeneracy.
|
|
8
|
+
|
|
9
|
+
Architecture
|
|
10
|
+
------------
|
|
11
|
+
The package is organized as four cooperating layers:
|
|
12
|
+
|
|
13
|
+
1. **context** — global algebra state (dimension, signature, degeneracy, grade table).
|
|
14
|
+
Call :func:`context.Initialize`, :func:`context.Layout`, or :func:`context.Cl`
|
|
15
|
+
to configure the algebra before constructing any multivectors.
|
|
16
|
+
|
|
17
|
+
2. **sign_table** — builds and caches the multiplication sign table for the current
|
|
18
|
+
algebra, and generates a Numba-jitted fast multiplier for dimensions < 8.
|
|
19
|
+
|
|
20
|
+
3. **multivector** — the :class:`multivector.Accum` class, which implements a
|
|
21
|
+
multivector as a NumPy array of real coefficients indexed by their ordinal
|
|
22
|
+
(a bitmask whose set bits name the basis vectors in the blade).
|
|
23
|
+
|
|
24
|
+
4. **util** — convenience functions for constructing, printing, and operating on
|
|
25
|
+
multivectors (grade projection, involutions, random multivectors, inverses
|
|
26
|
+
for specific dimensions).
|
|
27
|
+
|
|
28
|
+
5. **tools** — stand alone assistant functions
|
|
29
|
+
|
|
30
|
+
Inverse algorithms live in the :mod:`clif.inverse` sub-package:
|
|
31
|
+
:func:`clif.inverse.fls_inverse` (Faddeev–LeVerrier–Souriau based, with
|
|
32
|
+
:func:`even_inverse`/:func:`odd_inverse` variants and sparse counterparts),
|
|
33
|
+
:func:`clif.inverse.euclidean_inverse`, and
|
|
34
|
+
:func:`clif.inverse.newton_schulz` (iterative refinement).
|
|
35
|
+
|
|
36
|
+
Ordinal indexing convention
|
|
37
|
+
---------------------------
|
|
38
|
+
An element of the algebra is identified by a non-negative integer whose binary
|
|
39
|
+
representation names the basis vectors it contains. In a 3-dimensional algebra::
|
|
40
|
+
|
|
41
|
+
0 = 0b000 scalar (grade 0)
|
|
42
|
+
1 = 0b001 e1 (grade 1)
|
|
43
|
+
2 = 0b010 e2 (grade 1)
|
|
44
|
+
3 = 0b011 e1^e2 (grade 2)
|
|
45
|
+
4 = 0b100 e3 (grade 1)
|
|
46
|
+
5 = 0b101 e1^e3 (grade 2)
|
|
47
|
+
6 = 0b110 e2^e3 (grade 2)
|
|
48
|
+
7 = 0b111 e1^e2^e3 = I (grade 3, pseudoscalar)
|
|
49
|
+
|
|
50
|
+
This ordering differs from the graded lexicographic ordering used in the public
|
|
51
|
+
``clifford`` package (pygae) and is a deliberate design choice of this implementation.
|
|
52
|
+
|
|
53
|
+
Quick start
|
|
54
|
+
-----------
|
|
55
|
+
::
|
|
56
|
+
|
|
57
|
+
from clif.context import Cl, Layout
|
|
58
|
+
from clif.multivector import Accum
|
|
59
|
+
|
|
60
|
+
Cl(1, 1, 1) # algebra with 1 positive, 1 negative, 1 null basis vector
|
|
61
|
+
A = Accum.random()
|
|
62
|
+
print(A)
|
|
63
|
+
|
|
64
|
+
Cl(1, 3) # spacetime algebra Cl(1,3)
|
|
65
|
+
Layout([1, -1, -1, -1]) # equivalent, explicit
|
|
66
|
+
|
|
67
|
+
Authors
|
|
68
|
+
-------
|
|
69
|
+
D. A. Jones (original implementation)
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
# ---- nice for the developer ---- (maybe)
|
|
73
|
+
from clif import context
|
|
74
|
+
from clif import sign_table
|
|
75
|
+
from clif import multivector
|
|
76
|
+
from clif import util
|
|
77
|
+
from clif import tools
|
|
78
|
+
|
|
79
|
+
# ---- all a user should need ----
|
|
80
|
+
from clif.context import Cl, Grade
|
|
81
|
+
from clif.multivector import Accum
|
|
82
|
+
|
|
83
|
+
__all__ = [
|
|
84
|
+
"context",
|
|
85
|
+
"sign_table",
|
|
86
|
+
"multivector",
|
|
87
|
+
"util",
|
|
88
|
+
"tools",
|
|
89
|
+
"Cl",
|
|
90
|
+
"Grade",
|
|
91
|
+
"Accum"
|
|
92
|
+
]
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# file: clif/context.py
|
|
2
|
+
"""
|
|
3
|
+
Algebra context — global state for the current Clifford algebra.
|
|
4
|
+
|
|
5
|
+
This module holds the parameters that define the algebra in use: the number of
|
|
6
|
+
dimensions, the metric signature (which basis vectors square to −1), and the
|
|
7
|
+
degeneracy mask (which basis vectors square to 0). It also maintains the
|
|
8
|
+
pre-computed :data:`Grade` table and the active :class:`~clif.sign_table.SignTable`.
|
|
9
|
+
|
|
10
|
+
All multivectors created after a call to :func:`Initialize` (or the convenience
|
|
11
|
+
wrappers :func:`Layout` and :func:`Cl`) share the same algebra context.
|
|
12
|
+
|
|
13
|
+
.. warning::
|
|
14
|
+
|
|
15
|
+
The context is **module-level mutable state**. Two multivectors created
|
|
16
|
+
under different :func:`Initialize` calls are mathematically incompatible.
|
|
17
|
+
Each :class:`~clif.multivector.Accum` instance records the context at
|
|
18
|
+
the time of its creation; use :func:`Check` to verify compatibility before
|
|
19
|
+
operating on a pair.
|
|
20
|
+
|
|
21
|
+
Initialisation functions
|
|
22
|
+
------------------------
|
|
23
|
+
Three entry points are provided for different workflows:
|
|
24
|
+
|
|
25
|
+
* :func:`Initialize` — specify dimension, signature mask, and degeneracy mask
|
|
26
|
+
directly (low-level, most explicit).
|
|
27
|
+
* :func:`Layout` — specify the metric signature as a list of ``{+1, 0, −1}``
|
|
28
|
+
values, one per basis vector.
|
|
29
|
+
* :func:`Cl` — specify the algebra as ``Cl(p, q, r)`` — ``p`` positive,
|
|
30
|
+
``q`` negative, ``r`` degenerate basis vectors.
|
|
31
|
+
|
|
32
|
+
Examples
|
|
33
|
+
--------
|
|
34
|
+
Euclidean 3-space::
|
|
35
|
+
|
|
36
|
+
import clif.context as Clif
|
|
37
|
+
Clif.Cl(3) # Cl(3, 0, 0)
|
|
38
|
+
|
|
39
|
+
Minkowski spacetime (one time-like dimension)::
|
|
40
|
+
|
|
41
|
+
Clif.Cl(1, 3) # Cl(1, 3, 0)
|
|
42
|
+
|
|
43
|
+
Projective geometry (one degenerate dimension)::
|
|
44
|
+
|
|
45
|
+
Clif.Cl(3, 0, 1) # Cl(3, 0, 1)
|
|
46
|
+
|
|
47
|
+
Explicit per-axis metric::
|
|
48
|
+
|
|
49
|
+
Clif.Layout([1, 1, -1, 0])
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
import numpy as np
|
|
53
|
+
import clif.sign_table as ST
|
|
54
|
+
# NOTE: clif.inverse is imported lazily inside Initialize(). A top-level
|
|
55
|
+
# import here would be circular: context -> inverse/__init__ -> fls -> context.
|
|
56
|
+
|
|
57
|
+
# ---------------------------------------------------------------------------
|
|
58
|
+
# Module-level state
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
#: Number of basis vectors in the current algebra.
|
|
62
|
+
dimensions: int = 0
|
|
63
|
+
|
|
64
|
+
#: Bitmask selecting the basis vectors that square to −1.
|
|
65
|
+
#: Bit *k* is set when basis vector *e*\ (k+1) has negative signature.
|
|
66
|
+
signature: int = 0
|
|
67
|
+
|
|
68
|
+
#: Bitmask selecting the basis vectors that square to 0 (degenerate/null).
|
|
69
|
+
#: Bit *k* is set when basis vector *e*\ (k+1) is degenerate.
|
|
70
|
+
degenerate: int = 0
|
|
71
|
+
|
|
72
|
+
#: Grade table: ``Grade[i]`` is the number of set bits in ``i``, i.e. the
|
|
73
|
+
#: grade of the blade whose ordinal index is ``i``.
|
|
74
|
+
#: Recomputed by :func:`Initialize`.
|
|
75
|
+
Grade: np.ndarray = []
|
|
76
|
+
|
|
77
|
+
#: The active :class:`~clif.sign_table.SignTable` for the current algebra.
|
|
78
|
+
#: Set by :func:`Initialize`; ``None`` until the first call.
|
|
79
|
+
_ActiveTable = None
|
|
80
|
+
|
|
81
|
+
_ActiveInverse = None
|
|
82
|
+
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
# Derived quantities
|
|
85
|
+
# ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
def bases() -> int:
|
|
88
|
+
"""Return the number of basis blades in the current algebra.
|
|
89
|
+
|
|
90
|
+
Equal to ``2 ** dimensions``.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
int
|
|
95
|
+
The number of basis blades.
|
|
96
|
+
|
|
97
|
+
Examples
|
|
98
|
+
--------
|
|
99
|
+
::
|
|
100
|
+
|
|
101
|
+
Clif.Cl(3)
|
|
102
|
+
assert Clif.bases() == 8 # scalar + 3 vectors + 3 bivectors + 1 trivector
|
|
103
|
+
"""
|
|
104
|
+
return 1 << dimensions
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def mask() -> int:
|
|
108
|
+
"""Return a bitmask covering all valid ordinal indices.
|
|
109
|
+
|
|
110
|
+
Equal to ``bases() - 1``.
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
int
|
|
115
|
+
Bitmask with the lowest ``dimensions`` bits set.
|
|
116
|
+
"""
|
|
117
|
+
return (1 << dimensions) - 1
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# ---------------------------------------------------------------------------
|
|
121
|
+
# Internal helpers
|
|
122
|
+
# ---------------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
def _build_grade_table( dimensions: int ) -> np.ndarray:
|
|
125
|
+
"""Recompute the :data:`Grade` table for the current :data:`dimensions`.
|
|
126
|
+
|
|
127
|
+
``Grade[i]`` equals the number of 1-bits (population count) in ``i``,
|
|
128
|
+
which is the grade of the blade with ordinal index ``i``.
|
|
129
|
+
|
|
130
|
+
This function modifies the module global :data:`Grade` in place.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
table = np.empty( 1<<dimensions, dtype=np.uint8 )
|
|
134
|
+
table[0] = 0
|
|
135
|
+
for d in range( dimensions ):
|
|
136
|
+
m = 1<<d
|
|
137
|
+
for n in range( m ):
|
|
138
|
+
table[m + n] = 1 + table[n]
|
|
139
|
+
return table
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# ---------------------------------------------------------------------------
|
|
143
|
+
# Initialisation entry points
|
|
144
|
+
# ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
def Initialize(d: int, s: int = 0, x: int = 0) -> None:
|
|
147
|
+
"""Configure the algebra by specifying dimension and metric masks.
|
|
148
|
+
|
|
149
|
+
This is the lowest-level initialisation entry point. :func:`Layout` and
|
|
150
|
+
:func:`Cl` are more convenient for most uses.
|
|
151
|
+
|
|
152
|
+
Parameters
|
|
153
|
+
----------
|
|
154
|
+
d : int
|
|
155
|
+
Number of basis vectors (dimensions of the vector space).
|
|
156
|
+
s : int, optional
|
|
157
|
+
Signature mask. Bit *k* is set when basis vector *e*\\ (k+1) squares
|
|
158
|
+
to −1. Default ``0`` gives a fully Euclidean algebra.
|
|
159
|
+
x : int, optional
|
|
160
|
+
Degeneracy mask. Bit *k* is set when basis vector *e*\\ (k+1) squares
|
|
161
|
+
to 0. Default ``0`` gives a non-degenerate algebra.
|
|
162
|
+
|
|
163
|
+
Notes
|
|
164
|
+
-----
|
|
165
|
+
Calling this function rebuilds the grade table and instantiates a new
|
|
166
|
+
:class:`~clif.sign_table.SignTable`. For dimensions ≥ 8 the sign
|
|
167
|
+
table is not pre-built (the Numba multiplier is not generated); a
|
|
168
|
+
different multiplication strategy is required for those cases.
|
|
169
|
+
|
|
170
|
+
Examples
|
|
171
|
+
--------
|
|
172
|
+
Euclidean 6-space::
|
|
173
|
+
|
|
174
|
+
Initialize(6)
|
|
175
|
+
|
|
176
|
+
Minkowski space (negative first axis)::
|
|
177
|
+
|
|
178
|
+
Initialize(4, s=0b0001)
|
|
179
|
+
|
|
180
|
+
Degenerate axis (projective)::
|
|
181
|
+
|
|
182
|
+
Initialize(4, x=0b1000)
|
|
183
|
+
"""
|
|
184
|
+
global dimensions, signature, degenerate, Grade, _ActiveTable, _ActiveInverse
|
|
185
|
+
dimensions = d
|
|
186
|
+
signature = s
|
|
187
|
+
degenerate = x
|
|
188
|
+
Grade = _build_grade_table( d )
|
|
189
|
+
_ActiveTable = ST.SignTable(d, s, x)
|
|
190
|
+
|
|
191
|
+
from clif.inverse import tau_api # local: breaks the cycle
|
|
192
|
+
try:
|
|
193
|
+
_ActiveInverse = tau_api.get_inverter(get_signature()).inverse
|
|
194
|
+
except AssertionError:
|
|
195
|
+
# sparse_fls is the single source of truth on what it supports
|
|
196
|
+
# (currently d = 4..11, non-degenerate). Anything else: no inverse.
|
|
197
|
+
_ActiveInverse = None
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
def Layout(Signature: list) -> None:
|
|
201
|
+
"""Configure the algebra from a per-axis metric list.
|
|
202
|
+
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
Signature : list of int
|
|
206
|
+
A list of ``+1``, ``0``, or ``-1`` values, one per basis vector.
|
|
207
|
+
The first entry describes *e1*, the second *e2*, and so on.
|
|
208
|
+
|
|
209
|
+
Examples
|
|
210
|
+
--------
|
|
211
|
+
Euclidean 3D::
|
|
212
|
+
|
|
213
|
+
Layout([1, 1, 1])
|
|
214
|
+
|
|
215
|
+
Minkowski (time-first)::
|
|
216
|
+
|
|
217
|
+
Layout([1, -1, -1, -1])
|
|
218
|
+
|
|
219
|
+
Projective 3D (degenerate first axis)::
|
|
220
|
+
|
|
221
|
+
Layout([0, 1, 1, 1])
|
|
222
|
+
"""
|
|
223
|
+
d = len(Signature)
|
|
224
|
+
s = 0
|
|
225
|
+
x = 0
|
|
226
|
+
for w in Signature[::-1]:
|
|
227
|
+
s <<= 1
|
|
228
|
+
x <<= 1
|
|
229
|
+
if w < 0:
|
|
230
|
+
s |= 1
|
|
231
|
+
elif w == 0:
|
|
232
|
+
x |= 1
|
|
233
|
+
Initialize(d, s, x)
|
|
234
|
+
return
|
|
235
|
+
|
|
236
|
+
def get_signature():
|
|
237
|
+
global dimensions
|
|
238
|
+
global signature
|
|
239
|
+
global degenerate
|
|
240
|
+
layout = []
|
|
241
|
+
for d in range(dimensions):
|
|
242
|
+
mask = 1 << d
|
|
243
|
+
if 0 != degenerate & mask:
|
|
244
|
+
this1 = 0
|
|
245
|
+
elif 0 != signature & mask:
|
|
246
|
+
this1 = -1
|
|
247
|
+
else:
|
|
248
|
+
this1 = 1
|
|
249
|
+
layout += [ this1 ]
|
|
250
|
+
return layout
|
|
251
|
+
|
|
252
|
+
def Cl(p: int, q: int = 0, r: int = 0) -> None:
|
|
253
|
+
"""Configure the algebra as Cl(p, q, r).
|
|
254
|
+
|
|
255
|
+
Constructs the Clifford algebra with ``p`` basis vectors squaring to +1,
|
|
256
|
+
``q`` squaring to −1, and ``r`` squaring to 0, in that order.
|
|
257
|
+
|
|
258
|
+
Parameters
|
|
259
|
+
----------
|
|
260
|
+
p : int
|
|
261
|
+
Number of positive-signature (Euclidean) basis vectors.
|
|
262
|
+
q : int, optional
|
|
263
|
+
Number of negative-signature basis vectors. Default 0.
|
|
264
|
+
r : int, optional
|
|
265
|
+
Number of degenerate (null) basis vectors. Default 0.
|
|
266
|
+
|
|
267
|
+
Examples
|
|
268
|
+
--------
|
|
269
|
+
Euclidean 3-space ``Cl(3, 0)``::
|
|
270
|
+
|
|
271
|
+
Cl(3)
|
|
272
|
+
|
|
273
|
+
Spacetime algebra ``Cl(1, 3)``::
|
|
274
|
+
|
|
275
|
+
Cl(1, 3)
|
|
276
|
+
|
|
277
|
+
Conformal model of 3D space ``Cl(4, 1)``::
|
|
278
|
+
|
|
279
|
+
Cl(4, 1)
|
|
280
|
+
"""
|
|
281
|
+
Layout( [1]*p + [-1]*q + [0]*r )
|
|
282
|
+
return
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
# ---------------------------------------------------------------------------
|
|
286
|
+
# Compatibility check
|
|
287
|
+
# ---------------------------------------------------------------------------
|
|
288
|
+
|
|
289
|
+
def Check(A, B) -> None:
|
|
290
|
+
"""Assert that two multivectors share the same algebra context.
|
|
291
|
+
|
|
292
|
+
Parameters
|
|
293
|
+
----------
|
|
294
|
+
A, B : :class:`~clif.multivector.Accum`
|
|
295
|
+
The two multivectors to compare.
|
|
296
|
+
|
|
297
|
+
Raises
|
|
298
|
+
------
|
|
299
|
+
AssertionError
|
|
300
|
+
If the multivectors differ in dimension, signature, or degeneracy.
|
|
301
|
+
|
|
302
|
+
Notes
|
|
303
|
+
-----
|
|
304
|
+
This check is *not* called automatically during arithmetic for performance
|
|
305
|
+
reasons. Call it explicitly during debugging or testing.
|
|
306
|
+
"""
|
|
307
|
+
assert A.dimensions == B.dimensions, \
|
|
308
|
+
f"Dimension mismatch: {A.dimensions} vs {B.dimensions}"
|
|
309
|
+
assert A.signature == B.signature, \
|
|
310
|
+
f"Signature mismatch: {A.signature:#010b} vs {B.signature:#010b}"
|
|
311
|
+
assert A.degenerate == B.degenerate, \
|
|
312
|
+
f"Degeneracy mismatch: {A.degenerate:#010b} vs {B.degenerate:#010b}"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# file: clif/inverse/__init__.py
|
|
2
|
+
# 2026.05.14 DAJones: created
|
|
3
|
+
# 2026.08.17 DAJones: updated sparse_fls and added tau_api
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
clif.inverse
|
|
7
|
+
~~~~~~~~~~~~
|
|
8
|
+
Fast, JIT-compiled multivector inverse routines for arbitrary signature.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .fls import fls_inverse
|
|
12
|
+
from .newton import newton_schulz
|
|
13
|
+
from .euclidean import euclidean_inverse
|
|
14
|
+
from .sparse_fls import TauInverse
|
|
15
|
+
from .sparse_fls_dd import DDTauInverse
|
|
16
|
+
from .tau_api import get_inverter, inverse, warm
|
|
17
|
+
from .low_dim_inverse import inverse
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"euclidean_inverse",
|
|
21
|
+
"fls_inverse",
|
|
22
|
+
"newton_schulz",
|
|
23
|
+
"TauInverse",
|
|
24
|
+
"get_inverter", "inverse", "warm"
|
|
25
|
+
"inverse"
|
|
26
|
+
]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# file: clif/inverse/euclidean.py
|
|
2
|
+
"""
|
|
3
|
+
Top-level multivector inverse via the tau-domain kernel, d in 4-11.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import clif.context as Clif
|
|
7
|
+
from clif.multivector import Accum
|
|
8
|
+
from clif.inverse.tau_api import get_inverter
|
|
9
|
+
from clif.inverse.newton import newton_schulz
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def euclidean_inverse(A: Accum, refine: bool = True) -> Accum:
|
|
13
|
+
"""Inverse of a non-singular multivector in Euclidean Cl(d, 0), d in 6-13.
|
|
14
|
+
|
|
15
|
+
Uses the sparse FLS kernel for speed. At dimensions 12-13 the
|
|
16
|
+
polynomial iteration accumulates enough floating-point error that
|
|
17
|
+
the raw result has residuals of O(10⁻⁵) to O(10⁻³); when
|
|
18
|
+
``refine=True`` (the default) Newton-Schulz iteration is applied
|
|
19
|
+
automatically to recover machine precision.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
A : Accum
|
|
24
|
+
Non-singular multivector.
|
|
25
|
+
refine : bool, optional
|
|
26
|
+
If True (default), apply Newton-Schulz refinement for dim >= 12.
|
|
27
|
+
Set False to return the raw FLS result at any dimension.
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
Accum
|
|
32
|
+
Multiplicative inverse of A.
|
|
33
|
+
|
|
34
|
+
Raises
|
|
35
|
+
------
|
|
36
|
+
ValueError
|
|
37
|
+
If dimension is outside 6-13.
|
|
38
|
+
|
|
39
|
+
Notes
|
|
40
|
+
-----
|
|
41
|
+
Typical residuals (max |A · A⁻¹ − e₀| over all blades):
|
|
42
|
+
|
|
43
|
+
====== ============ ===============
|
|
44
|
+
dim FLS only FLS + NS
|
|
45
|
+
====== ============ ===============
|
|
46
|
+
6 1.4 × 10⁻¹⁴ —
|
|
47
|
+
7 1.7 × 10⁻¹⁵ —
|
|
48
|
+
8 2.4 × 10⁻¹⁴ —
|
|
49
|
+
9 1.8 × 10⁻¹² —
|
|
50
|
+
10 3.6 × 10⁻¹⁰ —
|
|
51
|
+
11 2.9 × 10⁻¹⁰ —
|
|
52
|
+
12 9.4 × 10⁻⁶ 9.0 × 10⁻¹⁶
|
|
53
|
+
13 4.2 × 10⁻³ 1.8 × 10⁻¹⁵
|
|
54
|
+
====== ============ ===============
|
|
55
|
+
"""
|
|
56
|
+
X = Accum()
|
|
57
|
+
X.Reg = get_inverter(Clif.get_signature()).inverse(A.Reg)
|
|
58
|
+
if refine and A.dimensions >= 12:
|
|
59
|
+
X = newton_schulz(A, X)
|
|
60
|
+
return X
|