From: Michael Orlitzky Date: Fri, 20 Feb 2026 19:08:26 +0000 (-0500) Subject: mjo/clan/clan_operator.py: implement composition and related methods X-Git-Url: https://gitweb.michael.orlitzky.com/?a=commitdiff_plain;h=4ff9def44b29bdddeaddbe632a469907eceb10a7;p=sage.d.git mjo/clan/clan_operator.py: implement composition and related methods --- diff --git a/mjo/clan/clan_operator.py b/mjo/clan/clan_operator.py index 33ff323..5f88d17 100644 --- a/mjo/clan/clan_operator.py +++ b/mjo/clan/clan_operator.py @@ -84,6 +84,158 @@ class ClanOperator(Map): """ return self.codomain().from_vector(self.matrix()*x.to_vector()) + def _composition_(self, other, homset): + """ + Compose two clan operators to get another one (and NOT a formal + composite object) back. + + SETUP:: + + sage: from mjo.clan.clan_operator import ClanOperator + sage: from mjo.clan.clan import HnClan, SnClan, VinbergClan + + EXAMPLES:: + + sage: C1 = VinbergClan() + sage: C2 = HnClan(2) + sage: C3 = SnClan(2) + sage: mat1 = matrix(QQ, [[1,2,3,4,5], + ....: [6,7,8,9,0], + ....: [1,2,3,4,5], + ....: [6,7,8,9,0]]) + sage: mat2 = matrix(QQ, [[1,2,3,4], + ....: [5,6,7,8], + ....: [9,0,1,2]]) + sage: g = ClanOperator(C1, C2, mat1) + sage: f = ClanOperator(C2, C3, mat2) + sage: f*g + Clan operator represented by the matrix: + [ 40 50 60 70 20] + [ 96 122 148 174 60] + [ 22 34 46 58 50] + Domain: Vinberg clan over Rational Field + Codomain: Clan S^2 over Rational Field + + """ + return ClanOperator( + other.domain(), + self.codomain(), + self.matrix()*other.matrix()) + + def __mul__(self, other): + """ + Compose two clan operators, or scale myself by an element of the + ambient vector space. + + We need to override the real ``__mul__`` function to prevent the + coercion framework from throwing an error when it fails to convert + a base ring element into a morphism. + + SETUP:: + + sage: from mjo.clan.clan import HnClan + sage: from mjo.clan.clan_operator import ClanOperator + + EXAMPLES: + + We can scale an operator on a rational algebra by a rational number:: + + sage: C = HnClan(2) + sage: b0,b1,b2,b3 = C.gens() + sage: x = 4*b0 + 8*b1 + 32*b2 + 64*b3 + sage: x.operator() + Clan operator represented by the matrix: + [ 4 0 0 0] + [ 8 34 0 0] + [32 0 34 0] + [ 0 16 64 64] + Domain: Clan H^2 over Rational Field + Codomain: Clan H^2 over Rational Field + sage: x.operator()*(1/2) + Clan operator represented by the matrix: + [ 2 0 0 0] + [ 4 17 0 0] + [16 0 17 0] + [ 0 8 32 32] + Domain: Clan H^2 over Rational Field + Codomain: Clan H^2 over Rational Field + + """ + try: + if other in self.codomain().base_ring(): + return ClanOperator( + self.domain(), + self.codomain(), + self.matrix()*other) + except NotImplementedError: + # This can happen with certain arguments if the base_ring() + # is weird and doesn't know how to test membership. + pass + + # This should eventually delegate to _composition_ after performing + # some sanity checks for us. + return super().__mul__(other) + + def __pow__(self, n): + """ + Raise this clan operator to the power ``n``. + + SETUP:: + + sage: from mjo.clan.clan_operator import ClanOperator + sage: from mjo.clan.clan import HnClan, SnClan + + EXAMPLES:: + + sage: C = SnClan(2) + sage: f = C.random_element().operator() + sage: f^0 + Clan operator represented by the matrix: + [1 0 0] + [0 1 0] + [0 0 1] + Domain: Clan S^2 over Rational Field + Codomain: Clan S^2 over Rational Field + sage: id = C.one().operator() + sage: id^2 + Clan operator represented by the matrix: + [1 0 0] + [0 1 0] + [0 0 1] + Domain: Clan S^2 over Rational Field + Codomain: Clan S^2 over Rational Field + + TESTS: + + Exponentiation doesn't work when the domain and codomain + differ, even if their dimensions are compatible:: + + sage: C1 = SnClan(1) + sage: C2 = HnClan(1) + sage: I = C1.one().operator().matrix() + sage: L = ClanOperator(C1, C2, I) + sage: L^2 + Traceback (most recent call last): + ... + TypeError: ...domain must equal right... + + """ + if (n == 1): + return self + elif (n == 0): + # Raising a vector space morphism to the zero power gives + # you back a special IdentityMorphism that is useless to us. + mat = self.matrix()**self.base_ring().zero() + return ClanOperator(self.domain(), self.codomain(), mat) + + # Actually multiply them for n >= 2 so that the domain and + # codomain checks in __mul__ aren't skipped (as they would be + # if we simply exponentiated the matrix). + from functools import reduce + from itertools import repeat + from operator import mul + return reduce(mul, repeat(self, n)) + def _repr_(self): r"""