Skip to content

Commit d7e271c

Browse files
author
Release Manager
committed
gh-36913: adding corolla-related methods to free pre-Lie algebras This is adding a few useful methods in the free pre-Lie algebras. In particular, an approximate version of the preLie Baker-Campbell- Hausdorff formula. ### 📝 Checklist - [x] The title is concise, informative, and self-explanatory. - [x] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [x] I have created tests covering the changes. - [x] I have updated the documentation accordingly. URL: #36913 Reported by: Frédéric Chapoton Reviewer(s): Travis Scrimshaw
2 parents 8432095 + f72a51a commit d7e271c

File tree

1 file changed

+269
-0
lines changed

1 file changed

+269
-0
lines changed

src/sage/combinat/free_prelie_algebra.py

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# the License, or (at your option) any later version.
1616
# https://www.gnu.org/licenses/
1717
# ****************************************************************************
18+
from itertools import product
1819

1920
from sage.categories.magmatic_algebras import MagmaticAlgebras
2021
from sage.categories.lie_algebras import LieAlgebras
@@ -26,6 +27,7 @@
2627
from sage.categories.functor import Functor
2728

2829
from sage.combinat.free_module import CombinatorialFreeModule
30+
from sage.combinat.integer_vector import IntegerVectors
2931
from sage.combinat.words.alphabet import Alphabet
3032
from sage.combinat.rooted_tree import (RootedTrees, RootedTree,
3133
LabelledRootedTrees,
@@ -34,6 +36,7 @@
3436

3537
from sage.misc.lazy_attribute import lazy_attribute
3638
from sage.misc.cachefunc import cached_method
39+
from sage.functions.other import factorial
3740

3841
from sage.sets.family import Family
3942
from sage.structure.coerce_exceptions import CoercionException
@@ -530,6 +533,135 @@ def nap_product(self):
530533
codomain=self),
531534
position=1)
532535

536+
def corolla(self, x, y, n, N):
537+
"""
538+
Return the corolla obtained with ``x`` as root and ``y`` as leaves.
539+
540+
INPUT:
541+
542+
- ``x``, ``y`` -- two elements
543+
- ``n`` -- integer; width of the corolla
544+
- ``N`` -- integer; truncation order (up to order ``N`` included)
545+
546+
OUTPUT:
547+
548+
the sum over all possible ways to graft ``n`` copies of ``y``
549+
on top of ``x`` (with at most ``N`` vertices in total)
550+
551+
This operation can be defined by induction starting from the
552+
pre-Lie product.
553+
554+
EXAMPLES::
555+
556+
sage: A = algebras.FreePreLie(QQ)
557+
sage: a = A.gen(0)
558+
sage: b = A.corolla(a,a,1,4); b
559+
B[[[]]]
560+
sage: A.corolla(b,b,2,7)
561+
B[[[[[]], [[]]]]] + 2*B[[[[]], [[[]]]]] + B[[[], [[]], [[]]]]
562+
563+
sage: A = algebras.FreePreLie(QQ, 'o')
564+
sage: a = A.gen(0)
565+
sage: b = A.corolla(a,a,1,4)
566+
567+
sage: A = algebras.FreePreLie(QQ,'ab')
568+
sage: a, b = A.gens()
569+
sage: A.corolla(a,b,1,4)
570+
B[a[b[]]]
571+
sage: A.corolla(b,a,3,4)
572+
B[b[a[], a[], a[]]]
573+
574+
sage: A.corolla(a+b,a+b,2,4)
575+
B[a[a[], a[]]] + 2*B[a[a[], b[]]] + B[a[b[], b[]]] + B[b[a[], a[]]] +
576+
2*B[b[a[], b[]]] + B[b[b[], b[]]]
577+
578+
TESTS::
579+
580+
sage: A = algebras.FreePreLie(QQ,'ab')
581+
sage: a, b = A.gens()
582+
sage: A.corolla(a,A.zero(),2,2)
583+
0
584+
"""
585+
if not x or not y:
586+
return self.zero()
587+
588+
basering = self.base_ring()
589+
vx = x.valuation()
590+
vy = y.valuation()
591+
min_deg = vy * n + vx
592+
if min_deg > N:
593+
return self.zero()
594+
595+
try:
596+
self.gen(0).support()[0].label()
597+
labels = True
598+
except AttributeError:
599+
labels = False
600+
601+
deg_x = x.maximal_degree()
602+
deg_y = y.maximal_degree()
603+
max_x = min(deg_x, N - n * vy)
604+
max_y = min(deg_y, N - vx - (n - 1) * vy)
605+
xx = x.truncate(max_x + 1)
606+
yy = y.truncate(max_y + 1)
607+
608+
y_homog = {i: list(yy.homogeneous_component(i))
609+
for i in range(vy, max_y + 1)}
610+
resu = self.zero()
611+
for k in range(min_deg, N + 1): # total degree of (x ; y, y, y, y)
612+
for mx, coef_x in xx:
613+
dx = mx.node_number()
614+
step = self.zero()
615+
for pi in IntegerVectors(k - dx, n, min_part=vy, max_part=max_y):
616+
for ly in product(*[y_homog[part] for part in pi]):
617+
coef_y = basering.prod(mc[1] for mc in ly)
618+
arbres_y = [mc[0] for mc in ly]
619+
step += coef_y * self.sum(self(t)
620+
for t in corolla_gen(mx, arbres_y, labels))
621+
resu += coef_x * step
622+
return resu
623+
624+
def group_product(self, x, y, n, N=10):
625+
r"""
626+
Return the truncated group product of ``x`` and ``y``.
627+
628+
This is a weighted sum of all corollas with up to ``n`` leaves, with
629+
``x`` as root and ``y`` as leaves.
630+
631+
The result is computed up to order ``N`` (included).
632+
633+
When considered with infinitely many terms and infinite precision,
634+
this is an analogue of the Baker-Campbell-Hausdorff formula: it
635+
defines an associative product on the completed free pre-Lie algebra.
636+
637+
INPUT:
638+
639+
- ``x``, ``y`` -- two elements
640+
- ``n`` -- integer; the maximal width of corollas
641+
- ``N`` -- integer (default: 10); truncation order
642+
643+
EXAMPLES:
644+
645+
In the free pre-Lie algebra with one generator::
646+
647+
sage: PL = algebras.FreePreLie(QQ)
648+
sage: a = PL.gen(0)
649+
sage: PL.group_product(a, a, 3, 3)
650+
B[[]] + B[[[]]] + 1/2*B[[[], []]]
651+
652+
In the free pre-Lie algebra with several generators::
653+
654+
sage: PL = algebras.FreePreLie(QQ,'@O')
655+
sage: a, b = PL.gens()
656+
sage: PL.group_product(a, b, 3, 3)
657+
B[@[]] + B[@[O[]]] + 1/2*B[@[O[], O[]]]
658+
sage: PL.group_product(a, b, 3, 10)
659+
B[@[]] + B[@[O[]]] + 1/2*B[@[O[], O[]]] + 1/6*B[@[O[], O[], O[]]]
660+
"""
661+
br = self.base_ring()
662+
return x + self.sum(self.corolla(x, y, i, N) * ~br(factorial(i))
663+
for i in range(1, n + 1))
664+
533665
def _element_constructor_(self, x):
534666
r"""
535667
Convert ``x`` into ``self``.
@@ -703,6 +835,40 @@ def lift(self):
703835
for x, cf in self.monomial_coefficients(copy=False).items()}
704836
return UEA.element_class(UEA, data)
705837

838+
def valuation(self):
839+
"""
840+
Return the valuation of ``self``.
841+
842+
EXAMPLES::
843+
844+
sage: a = algebras.FreePreLie(QQ).gen(0)
845+
sage: a.valuation()
846+
1
847+
sage: (a*a).valuation()
848+
2
849+
850+
sage: a, b = algebras.FreePreLie(QQ,'ab').gens()
851+
sage: (a+b).valuation()
852+
1
853+
sage: (a*b).valuation()
854+
2
855+
sage: (a*b+a).valuation()
856+
1
857+
858+
TESTS::
859+
860+
sage: z = algebras.FreePreLie(QQ).zero()
861+
sage: z.valuation()
862+
+Infinity
863+
"""
864+
if self == self.parent().zero():
865+
return Infinity
866+
i = 0
867+
while True:
868+
i += 1
869+
if self.homogeneous_component(i):
870+
return i
871+
706872

707873
class PreLieFunctor(ConstructionFunctor):
708874
"""
@@ -872,3 +1038,106 @@ def _repr_(self):
8721038
PreLie[x,y,z,t]
8731039
"""
8741040
return "PreLie[%s]" % ','.join(self.vars)
1041+
1042+
1043+
def tree_from_sortkey(ch, labels=True):
1044+
r"""
1045+
Transform a list of ``(valence, label)`` into a tree and a remainder.
1046+
1047+
This is like an inverse of the ``sort_key`` method.
1048+
1049+
INPUT:
1050+
1051+
- ``ch`` -- a list of pairs ``(integer, label)``
1052+
- ``labels`` -- (default ``True``) whether to use labelled trees
1053+
1054+
OUTPUT:
1055+
1056+
a pair ``(tree, remainder of the input)``
1057+
1058+
EXAMPLES::
1059+
1060+
sage: from sage.combinat.free_prelie_algebra import tree_from_sortkey
1061+
sage: a = algebras.FreePreLie(QQ).gen(0)
1062+
sage: t = (a*a*a*a).support()
1063+
sage: all(tree_from_sortkey(u.sort_key(), False)[0] == u for u in t)
1064+
True
1065+
1066+
sage: a, b = algebras.FreePreLie(QQ,'ab').gens()
1067+
sage: t = (a*b*a*b).support()
1068+
sage: all(tree_from_sortkey(u.sort_key())[0] == u for u in t)
1069+
True
1070+
"""
1071+
if labels:
1072+
Trees = LabelledRootedTrees()
1073+
width, label = ch[0]
1074+
else:
1075+
Trees = RootedTrees()
1076+
width = ch[0]
1077+
1078+
remainder = ch[1:]
1079+
if width == 0:
1080+
if labels:
1081+
return (Trees([], label), remainder)
1082+
return (Trees([]), remainder)
1083+
1084+
branches = {}
1085+
for i in range(width):
1086+
tree, remainder = tree_from_sortkey(remainder, labels=labels)
1087+
branches[i] = tree
1088+
1089+
if labels:
1090+
return (Trees(branches.values(), label), remainder)
1091+
return (Trees(branches.values()), remainder)
1092+
1093+
1094+
def corolla_gen(tx, list_ty, labels=True):
1095+
"""
1096+
Yield the terms in the corolla with given bottom tree and top trees.
1097+
1098+
These are the possible terms in the simultaneous grafting of the
1099+
top trees on vertices of the bottom tree.
1100+
1101+
INPUT:
1102+
1103+
- ``tx`` -- a tree
1104+
- ``list_ty`` -- a list of trees
1105+
1106+
EXAMPLES::
1107+
1108+
sage: from sage.combinat.free_prelie_algebra import corolla_gen
1109+
sage: a = algebras.FreePreLie(QQ).gen(0)
1110+
sage: ta = a.support()[0]
1111+
sage: list(corolla_gen(ta,[ta],False))
1112+
[[[]]]
1113+
1114+
sage: a, b = algebras.FreePreLie(QQ,'ab').gens()
1115+
sage: ta = a.support()[0]
1116+
sage: tb = b.support()[0]
1117+
sage: ab = (a*b).support()[0]
1118+
sage: list(corolla_gen(ta,[tb]))
1119+
[a[b[]]]
1120+
sage: list(corolla_gen(tb,[ta,ta]))
1121+
[b[a[], a[]]]
1122+
sage: list(corolla_gen(ab,[ab,ta]))
1123+
[a[a[], b[], a[b[]]], a[a[b[]], b[a[]]], a[a[], b[a[b[]]]],
1124+
a[b[a[], a[b[]]]]]
1125+
"""
1126+
n = len(list_ty)
1127+
zx = tx.sort_key()
1128+
nx = len(zx)
1129+
liste_zy = [t.sort_key() for t in list_ty]
1130+
for list_pos in product(range(nx), repeat=n):
1131+
new_zx = tuple(zx)
1132+
data = zip(list_pos, liste_zy)
1133+
sorted_data = sorted(data, reverse=True)
1134+
for pos_t in sorted_data:
1135+
if labels:
1136+
idx, lbl = new_zx[pos_t[0]]
1137+
new_zx = (new_zx[:pos_t[0]] + ((idx + 1, lbl),) +
1138+
pos_t[1] + new_zx[pos_t[0] + 1:])
1139+
else:
1140+
idx = new_zx[pos_t[0]]
1141+
new_zx = (new_zx[:pos_t[0]] + (idx + 1,) +
1142+
pos_t[1] + new_zx[pos_t[0] + 1:])
1143+
yield tree_from_sortkey(new_zx, labels=labels)[0]

0 commit comments

Comments
 (0)