]> gitweb.michael.orlitzky.com - sage.d.git/commitdiff
mjo/clan/unital_clan.py: add convenience constructors for Vinberg clans
authorMichael Orlitzky <michael@orlitzky.com>
Sun, 15 Feb 2026 16:33:43 +0000 (11:33 -0500)
committerMichael Orlitzky <michael@orlitzky.com>
Sun, 15 Feb 2026 16:33:43 +0000 (11:33 -0500)
Add the following methods to build an element of the Vinberg clan:

  * from_matrices
  * from_list
  * from_lists

mjo/clan/unital_clan.py

index c07404f87ad2514b4c1dd9467043f05e110d9b44..c560199ca0b33bde8f2b00237107f973d84da647 100644 (file)
@@ -710,3 +710,93 @@ class VinbergClan(NormalDecomposition):
 
         """
         return f"Vinberg clan over {self.base_ring()}"
+
+    def from_matrices(self, A, B):
+        r"""
+        Construct an element of this clan from a pair of
+        symmetric matrices.
+
+        EXAMPLES::
+
+            sage: C = VinbergClan()
+            sage: A = matrix(QQ, [ [2, 1],
+            ....:                  [1, 4] ])
+            sage: B = matrix(QQ, [ [2, 2],
+            ....:                  [2,-1] ])
+            sage: C.from_matrices(A,B).to_vector()
+            (2, 1, 4, 2, -1)
+
+        """
+        if not A.base_ring() == self.base_ring():
+            raise ValueError(f"base ring of matrix A ({A.base_ring()})"
+                             " does not match clan ({self.base_ring()})")
+        if not B.base_ring() == self.base_ring():
+            raise ValueError(f"base ring of matrix B ({A.base_ring()})"
+                             " does not match clan ({self.base_ring()})")
+        if not A == A.transpose():
+            raise ValueError("matrix A is not symmetric")
+        if not B == B.transpose():
+            raise ValueError("matrix B is not symmetric")
+        if not A.nrows() == 2:
+            raise ValueError(f"matrix A must be 2x2")
+        if not B.nrows() == 2:
+            raise ValueError(f"matrix B must be 2x2")
+        if not A[0,0] == B[0,0]:
+            raise ValueError(f"A and B must agree in the (0,0) position")
+
+        R5 = self._vector_space
+        return ( A[0,0]*R5((0,0,1)) +
+                 A[1,0]*R5((1,0,1)) +
+                 A[1,1]*R5((1,1,1)) +
+                 B[1,0]*R5((2,0,1)) +
+                 B[1,1]*R5((2,2,1)) )
+
+    def from_list(self, l):
+        r"""
+        Construct an element of this clan from a list.
+
+        This is a bit different from the other ``from_list`` methods
+        on matrix clans, because the underlying vector space for the
+        Vinberg clan is basically `R^5`. So this method just takes a
+        list of five numbers and returns a clan element. It is a
+        trivial wrapper around :meth:`from_vector`.
+
+        EXAMPLES::
+
+            sage: C = VinbergClan()
+            sage: x = C.from_list([2,1,4,2,-1])
+            sage: x.to_vector()
+            (2, 1, 4, 2, -1)
+
+        """
+        V = self._vector_space
+        return V.sum_of_terms(zip(V.basis().keys(), l))
+
+    def from_lists(self, l1, l2):
+        r
+        """
+        Construct an element of this clan from a pair of lists.
+
+        This is a shortcut for :meth:`from_matrix`, as the clan knows
+        what the ambient matrix space was.
+
+        EXAMPLES::
+
+            sage: C = VinbergClan()
+            sage: x = C.from_lists([[2,1],[1,4]], [[2,2],[2,-1]])
+            sage: x.to_vector()
+            (2, 1, 4, 2, -1)
+
+        This relies on the ambient vector space to convert a list to a
+        matrix, so we can use one long list (as opposed to a list of
+        lists) in each component::
+
+            sage: C = VinbergClan()
+            sage: x = C.from_lists([2,1,1,4],[2,2,2,-1])
+            sage: x.to_vector()
+            (2, 1, 4, 2, -1)
+
+        """
+        A = self._S2.ambient()(l1)
+        B = self._S2.ambient()(l2)
+        return self.from_matrices(A,B)