From 1f76a7e1f5d9fb840c7b64e50efc518d8815ea97 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 15 Feb 2026 11:33:43 -0500 Subject: [PATCH] mjo/clan/unital_clan.py: add convenience constructors for Vinberg clans Add the following methods to build an element of the Vinberg clan: * from_matrices * from_list * from_lists --- mjo/clan/unital_clan.py | 90 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/mjo/clan/unital_clan.py b/mjo/clan/unital_clan.py index c07404f..c560199 100644 --- a/mjo/clan/unital_clan.py +++ b/mjo/clan/unital_clan.py @@ -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) -- 2.51.0