]> gitweb.michael.orlitzky.com - sage.d.git/commitdiff
mjo/clan/jordan_spin_clan.py: support n=1 (the real numbers)
authorMichael Orlitzky <michael@orlitzky.com>
Wed, 4 Mar 2026 12:47:14 +0000 (07:47 -0500)
committerMichael Orlitzky <michael@orlitzky.com>
Wed, 4 Mar 2026 12:47:14 +0000 (07:47 -0500)
mjo/clan/jordan_spin_clan.py

index 7eabc84eba5f6285b7d730c718d40f9cfd013876..54fc6238f92b7fb4ad16f7a30e71ff304e6d1780 100644 (file)
@@ -9,10 +9,18 @@ class JordanSpinClan(NormalDecomposition):
     be a :class:`mjo.clan.t_algebra_clan.TAlgebraClan`, but it isn't
     implemented that way because the T-algebra is not readily
     available (we'd have to built it, too). The T-algebra formulation
-    is described in *T-algebras and linear optimization over symmetric
-    cones* by Chek Beng Chua, but the formula for the diagonals is actually
-    wrong. It has to be (a*b)_11 = a11*b11 + <a12,b21> and similarly
-    for (a*b)_22; otherwise, the identity isn't an identity.
+    is perhaps best described by [Gindikin1992]_ in his example (e),
+    on pages 89-90.
+
+    We make minor adjustments to support `n=1` as an argument.
+
+    REFERENCES:
+
+      .. [Gindikin1992] Simon Gindikin.
+         *Tube domains and the Cauchy problem*,
+         vol. 111 of Translations of Mathematical Monographs.
+         American Mathematical Society, Providence, Rhode Island, 1992.
+         ISBN 0821845667.
 
     SETUP::
 
@@ -35,6 +43,20 @@ class JordanSpinClan(NormalDecomposition):
 
     TESTS:
 
+    One-dimensional instances (the real numbers, essentially) work::
+
+        sage: C = JordanSpinClan(1); C
+        Jordan spin clan of dimension 1 over Rational Field
+        sage: C.dimension()
+        1
+        sage: C.basis()
+        Finite family {(0, 0, 1): B(0, 0, 1)}
+        sage: C.one()
+        B(0, 0, 1)
+        sage: x = C.from_vector(vector(QQ,[7]))
+        sage: x.D(0)
+        7
+
     Verifying the axioms::
 
         sage: n = ZZ.random_element(2,10)
@@ -71,34 +93,46 @@ class JordanSpinClan(NormalDecomposition):
     """
     from sage.rings.rational_field import QQ
     def __init__(self, n, scalar_field=QQ, **kwargs):
-        if n < 2:
-            raise ValueError("You want the real numbers?")
         from sage.modules.free_module import VectorSpace
 
-        # We need an Ishi basis (i,j,k) for R^N if we want to use
-        # NormalDecomposition.
-        indices = [(0,0,1)] + [(1,0,k) for k in range(1, n-1)] + [(1,1,1)]
-        RN = VectorSpace(scalar_field, indices)
+        if n == 1:
+            # The T-algebra construction requires n >= 2, but we
+            # know that in dimension one this clan corresponds to
+            # a single ray, and is essentially the real numbers.
+            indices = [(0,0,1)]
+        else:
+            # We need an Ishi basis (i,j,k) for R^N if we want to use
+            # NormalDecomposition.
+            indices = [(0,0,1)] + [(1,0,k) for k in range(1, n-1)] + [(1,1,1)]
 
+        RN = VectorSpace(scalar_field, indices)
         two = scalar_field(2)
 
         def cp(x,y):
-            # keep in mind, x and y will be (basis) elements of RN
+            # Keep in mind, x and y will be (basis) elements of RN
             x = x.to_vector()
-            x11 = x[0]
-            x_bar = x[1:-1]
-            x22 = x[-1]
             y = y.to_vector()
+            V = x.parent()
+
+            x11 = x[0]
             y11 = y[0]
+            z11 = x11*y11  # z = x*y
+
+            if n == 1:
+                return RN.from_vector(V([z11]))
+
+            # Will be empty for n=2
+            x_bar = x[1:-1]
             y_bar = y[1:-1]
+
+            x22 = x[-1]
             y22 = y[-1]
 
-            # z = x*y
-            V = x.parent()
-            z11 = x11*y11
+            # Computed by hand using down-hat and up-hat
             z21 = y_bar*(x11 + x22)/two + y11*x_bar
             z22 = x22*y22 + 2*x_bar.inner_product(y_bar)
             z_coords = [z11] + list(z21) + [z22]
+
             return RN.from_vector(V(z_coords))
 
         def ip(x,y):
@@ -110,7 +144,7 @@ class JordanSpinClan(NormalDecomposition):
             #
             # (The off-diagonals are weighted more than usual.)
             p = cp(x,y) / two
-            return p[(0,0,1)] + p[(1,1,1)]
+            return sum( p[idx] for idx in indices if idx[0] == idx[1] )
 
         super().__init__(RN, cp, ip, **kwargs)
 
@@ -131,4 +165,3 @@ class JordanSpinClan(NormalDecomposition):
         """
         return (f"Jordan spin clan of dimension {self.dimension()} "
                 f"over {self.base_ring()}")
-