From fe8b40ecb6752b078c54629fcf4498356d8eca43 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 16 Feb 2026 21:51:59 -0500 Subject: [PATCH] mjo/clan: refactor the type hierarchy Put the clan stuff in a top-level Clan class, and make UnitalClan a wrapper around that. This complicates everything a bit, but makes me feel better. --- mjo/clan/{unital_clan.py => clan.py} | 51 +++++++++++++++++++++++++--- mjo/clan/clan_element.py | 20 +++++------ mjo/clan/clan_operator.py | 27 +++++++-------- 3 files changed, 69 insertions(+), 29 deletions(-) rename mjo/clan/{unital_clan.py => clan.py} (94%) diff --git a/mjo/clan/unital_clan.py b/mjo/clan/clan.py similarity index 94% rename from mjo/clan/unital_clan.py rename to mjo/clan/clan.py index 3bc8b5d..9435f73 100644 --- a/mjo/clan/unital_clan.py +++ b/mjo/clan/clan.py @@ -1,6 +1,8 @@ r""" -Proper implementation of unital clans (real, finite dimensional -"compact" left-symmetric algebras with a unit element). +Proper implementation of clans (real, finite dimensional +"compact" left-symmetric algebras). These are in one-to-one +correspondence with homogeneous convex domains, and the unital clans +correspond to the homogeneous convex cones. """ from sage.categories.magmatic_algebras import MagmaticAlgebras @@ -11,7 +13,15 @@ from sage.rings.rational_field import QQ from mjo.clan.clan_element import (ClanElement, NormalDecompositionElement) -class UnitalClan(CombinatorialFreeModule): +class Clan(CombinatorialFreeModule): + r""" + Base class for clans. + + These correspond to homogeneous convex domains, and vice-versa. + This is needed to fill out the type hierarchy, but isn't very + useful on its own because all we care about are cones (which + correspond to unital clans). + """ Element = ClanElement def __init__(self, @@ -27,7 +37,7 @@ class UnitalClan(CombinatorialFreeModule): basis = vector_space.basis() category = MagmaticAlgebras(scalar_field).or_subcategory(category) - category = category.FiniteDimensional().WithBasis().Unital() + category = category.FiniteDimensional().WithBasis() # Compute the multiplication table. self._mt = { i: { j : clan_product(basis[i], basis[j]) @@ -92,6 +102,38 @@ class UnitalClan(CombinatorialFreeModule): return self._mt[i][j] +class UnitalClan(Clan): + r""" + A clan with a unit element. + + These correspond to homogeneous convex cones, and vice-versa. + Not much here except a constructor that ensures we are using + a ``Unital()`` category. + """ + def __init__(self, + vector_space, + clan_product, + inner_product, + scalar_field=QQ, + category=None, + prefix=None, + bracket=False): + + category = MagmaticAlgebras(scalar_field).or_subcategory(category) + category = category.FiniteDimensional().WithBasis().Unital() + + # Use the vector_space basis keys so we can convert + # easily between them. + super().__init__(vector_space, + clan_product, + inner_product, + scalar_field=scalar_field, + category=category, + prefix=prefix, + bracket=bracket) + + + class NormalDecomposition(UnitalClan): r""" @@ -183,6 +225,7 @@ class NormalDecomposition(UnitalClan): """ return self._rank + class MatrixClan(NormalDecomposition): r""" A clan arising from a T-algebra of Hermitian matrices. diff --git a/mjo/clan/clan_element.py b/mjo/clan/clan_element.py index e550119..f4869a2 100644 --- a/mjo/clan/clan_element.py +++ b/mjo/clan/clan_element.py @@ -24,7 +24,7 @@ class ClanElement(IndexedFreeModuleElement): SETUP:: - sage: from mjo.clan.unital_clan import SnClan + sage: from mjo.clan.clan import SnClan EXAMPLES: @@ -54,7 +54,7 @@ class ClanElement(IndexedFreeModuleElement): SETUP:: - sage: from mjo.clan.unital_clan import SnClan + sage: from mjo.clan.clan import SnClan EXAMPLES: @@ -81,7 +81,7 @@ class ClanElement(IndexedFreeModuleElement): SETUP:: - sage: from mjo.clan.unital_clan import VinbergClan + sage: from mjo.clan.clan import VinbergClan EXAMPLES:: @@ -106,7 +106,7 @@ class ClanElement(IndexedFreeModuleElement): SETUP:: - sage: from mjo.clan.unital_clan import VinbergClan + sage: from mjo.clan.clan import VinbergClan EXAMPLES:: @@ -155,7 +155,7 @@ class NormalDecompositionElement(ClanElement): SETUP:: - sage: from mjo.clan.unital_clan import SnClan + sage: from mjo.clan.clan import SnClan EXAMPLES:: @@ -188,7 +188,7 @@ class NormalDecompositionElement(ClanElement): SETUP:: - sage: from mjo.clan.unital_clan import SnClan + sage: from mjo.clan.clan import SnClan EXAMPLES:: @@ -233,7 +233,7 @@ class NormalDecompositionElement(ClanElement): SETUP:: - sage: from mjo.clan.unital_clan import SnClan + sage: from mjo.clan.clan import SnClan EXAMPLES:: @@ -284,7 +284,7 @@ class NormalDecompositionElement(ClanElement): SETUP:: - sage: from mjo.clan.unital_clan import SnClan + sage: from mjo.clan.clan import SnClan EXAMPLES: @@ -351,7 +351,7 @@ class NormalDecompositionElement(ClanElement): SETUP:: - sage: from mjo.clan.unital_clan import SnClan, VinbergClan + sage: from mjo.clan.clan import SnClan, VinbergClan EXAMPLES: @@ -413,7 +413,7 @@ class NormalDecompositionElement(ClanElement): SETUP:: - sage: from mjo.clan.unital_clan import SnClan + sage: from mjo.clan.clan import SnClan EXAMPLES: diff --git a/mjo/clan/clan_operator.py b/mjo/clan/clan_operator.py index ed4d6b3..33ff323 100644 --- a/mjo/clan/clan_operator.py +++ b/mjo/clan/clan_operator.py @@ -5,17 +5,14 @@ class ClanOperator(Map): r""" An operator between two clans. - Defined for *unital* clans at the moment, because that's all we - have. - SETUP:: - sage: from mjo.clan.unital_clan import SnClan + sage: from mjo.clan.clan import SnClan sage: from mjo.clan.clan_operator import ClanOperator EXAMPLES: - The domain and codomain must be unital clans; if either is not, + The domain and codomain must be clans; if either is not, then an error is raised:: sage: J = SnClan(2) @@ -24,21 +21,21 @@ class ClanOperator(Map): sage: ClanOperator(V,J,M) Traceback (most recent call last): ... - TypeError: domain must be a unital clan + TypeError: domain must be a clan sage: ClanOperator(J,V,M) Traceback (most recent call last): ... - TypeError: codomain must be a unital clan + TypeError: codomain must be a clan """ def __init__(self, domain, codomain, mat): - from mjo.clan.unital_clan import UnitalClan + from mjo.clan.clan import Clan - if not isinstance(domain, UnitalClan): - raise TypeError('domain must be a unital clan') - if not isinstance(codomain, UnitalClan): - raise TypeError('codomain must be a unital clan') + if not isinstance(domain, Clan): + raise TypeError('domain must be a clan') + if not isinstance(codomain, Clan): + raise TypeError('codomain must be a clan') F = domain.base_ring() if not (F == codomain.base_ring()): @@ -73,7 +70,7 @@ class ClanOperator(Map): SETUP:: sage: from mjo.clan.clan_operator import ClanOperator - sage: from mjo.clan.unital_clan import VinbergClan + sage: from mjo.clan.clan import VinbergClan EXAMPLES:: @@ -96,7 +93,7 @@ class ClanOperator(Map): SETUP:: sage: from mjo.clan.clan_operator import ClanOperator - sage: from mjo.clan.unital_clan import HnClan + sage: from mjo.clan.clan import HnClan EXAMPLES:: @@ -127,7 +124,7 @@ class ClanOperator(Map): SETUP:: sage: from mjo.clan.clan_operator import ClanOperator - sage: from mjo.clan.unital_clan import SnClan + sage: from mjo.clan.clan import SnClan EXAMPLES:: -- 2.51.0