|
15 | 15 | # the License, or (at your option) any later version.
|
16 | 16 | # https://www.gnu.org/licenses/
|
17 | 17 | # ****************************************************************************
|
| 18 | +from copy import copy |
| 19 | +from itertools import product |
18 | 20 |
|
19 | 21 | from sage.categories.magmatic_algebras import MagmaticAlgebras
|
20 | 22 | from sage.categories.lie_algebras import LieAlgebras
|
|
26 | 28 | from sage.categories.functor import Functor
|
27 | 29 |
|
28 | 30 | from sage.combinat.free_module import CombinatorialFreeModule
|
| 31 | +from sage.combinat.integer_vector import IntegerVectors |
29 | 32 | from sage.combinat.words.alphabet import Alphabet
|
30 | 33 | from sage.combinat.rooted_tree import (RootedTrees, RootedTree,
|
31 | 34 | LabelledRootedTrees,
|
|
34 | 37 |
|
35 | 38 | from sage.misc.lazy_attribute import lazy_attribute
|
36 | 39 | from sage.misc.cachefunc import cached_method
|
| 40 | +from sage.functions.other import factorial |
37 | 41 |
|
38 | 42 | from sage.sets.family import Family
|
39 | 43 | from sage.structure.coerce_exceptions import CoercionException
|
@@ -530,6 +534,138 @@ def nap_product(self):
|
530 | 534 | codomain=self),
|
531 | 535 | position=1)
|
532 | 536 |
|
| 537 | + def corolla(self, x, y, n, N): |
| 538 | + """ |
| 539 | + Return the corolla obtained with ``x`` as root and ``y`` as leaves. |
| 540 | +
|
| 541 | + INPUT: |
| 542 | +
|
| 543 | + - ``x``, ``y`` -- two elements |
| 544 | +
|
| 545 | + - ``n`` -- an integer, width of the corolla |
| 546 | +
|
| 547 | + - ``N`` -- an integer, truncation order (up to order ``N`` included) |
| 548 | +
|
| 549 | + OUTPUT: |
| 550 | +
|
| 551 | + the sum over all possible ways to graft ``n`` copies of ``y`` |
| 552 | + on top of ``x`` (with at most ``N`` vertices in total) |
| 553 | +
|
| 554 | + This operation can be defined by induction starting from the |
| 555 | + pre-Lie product. |
| 556 | +
|
| 557 | + EXAMPLES:: |
| 558 | +
|
| 559 | + sage: A = algebras.FreePreLie(QQ) |
| 560 | + sage: a = A.gen(0) |
| 561 | + sage: b = A.corolla(a,a,1,4); b |
| 562 | + B[[[]]] |
| 563 | + sage: A.corolla(b,b,2,7) |
| 564 | + B[[[[[]], [[]]]]] + 2*B[[[[]], [[[]]]]] + B[[[], [[]], [[]]]] |
| 565 | +
|
| 566 | + sage: A = algebras.FreePreLie(QQ, 'o') |
| 567 | + sage: a = A.gen(0) |
| 568 | + sage: b = A.corolla(a,a,1,4) |
| 569 | +
|
| 570 | + sage: A = algebras.FreePreLie(QQ,'ab') |
| 571 | + sage: a, b = A.gens() |
| 572 | + sage: A.corolla(a,b,1,4) |
| 573 | + B[a[b[]]] |
| 574 | + sage: A.corolla(b,a,3,4) |
| 575 | + B[b[a[], a[], a[]]] |
| 576 | +
|
| 577 | + sage: A.corolla(a+b,a+b,2,4) |
| 578 | + B[a[a[], a[]]] + 2*B[a[a[], b[]]] + B[a[b[], b[]]] + B[b[a[], a[]]] + |
| 579 | + 2*B[b[a[], b[]]] + B[b[b[], b[]]] |
| 580 | + """ |
| 581 | + basering = self.base_ring() |
| 582 | + if x == self.zero() or y == self.zero(): |
| 583 | + return self.zero() |
| 584 | + |
| 585 | + vx = x.valuation() |
| 586 | + vy = y.valuation() |
| 587 | + min_deg = vy * n + vx |
| 588 | + if min_deg > N: |
| 589 | + return self.zero() |
| 590 | + |
| 591 | + try: |
| 592 | + self.gen(0).support()[0].label() |
| 593 | + labels = True |
| 594 | + except AttributeError: |
| 595 | + labels = False |
| 596 | + |
| 597 | + deg_x = x.maximal_degree() |
| 598 | + deg_y = y.maximal_degree() |
| 599 | + max_x = min(deg_x, N - n * vy) |
| 600 | + max_y = min(deg_y, N - vx - (n - 1) * vy) |
| 601 | + xx = x.truncate(max_x + 1) |
| 602 | + yy = y.truncate(max_y + 1) |
| 603 | + |
| 604 | + y_homog = {i: list(yy.homogeneous_component(i)) |
| 605 | + for i in range(vy, max_y + 1)} |
| 606 | + resu = self.zero() |
| 607 | + for k in range(min_deg, N + 1): # total degree of (x ; y, y, y, y) |
| 608 | + for mx, coef_x in xx: |
| 609 | + dx = mx.node_number() |
| 610 | + step = self.zero() |
| 611 | + for pi in IntegerVectors(k - dx, n, min_part=vy, max_part=max_y): |
| 612 | + for ly in product(*[y_homog[part] for part in pi]): |
| 613 | + coef_y = basering.prod(mc[1] for mc in ly) |
| 614 | + arbres_y = [mc[0] for mc in ly] |
| 615 | + step += coef_y * self.sum(self(t) |
| 616 | + for t in corolla_gen(mx, |
| 617 | + arbres_y, |
| 618 | + labels)) |
| 619 | + resu += coef_x * step |
| 620 | + return resu |
| 621 | + |
| 622 | + def group_product(self, x, y, n, N=10): |
| 623 | + r""" |
| 624 | + Return the truncated group product of ``x`` and ``y``. |
| 625 | +
|
| 626 | + This is a weighted sum of all corollas with up to ``n`` leaves, with |
| 627 | + ``x`` as root and ``y`` as leaves. |
| 628 | +
|
| 629 | + The result is computed up to order ``N`` (included). |
| 630 | +
|
| 631 | + When considered with infinitely many terms and infinite precision, |
| 632 | + this is an analogue of the Baker-Campbell-Hausdorff formula: it |
| 633 | + defines an associative product on the free pre-Lie algebra. |
| 634 | +
|
| 635 | + INPUT: |
| 636 | +
|
| 637 | + - ``x``, ``y`` -- two elements |
| 638 | +
|
| 639 | + - ``n`` -- an integer, the maximal width of corollas |
| 640 | +
|
| 641 | + - ``N`` -- an integer (optional, default: 10), truncation order |
| 642 | +
|
| 643 | + OUTPUT: |
| 644 | +
|
| 645 | + an element |
| 646 | +
|
| 647 | + EXAMPLES: |
| 648 | +
|
| 649 | + In the free pre-Lie algebra with one generator:: |
| 650 | +
|
| 651 | + sage: PL = algebras.FreePreLie(QQ) |
| 652 | + sage: a = PL.gen(0) |
| 653 | + sage: PL.group_product(a, a, 3, 3) |
| 654 | + B[[]] + B[[[]]] + 1/2*B[[[], []]] |
| 655 | +
|
| 656 | + In the free pre-Lie algebra with several generators:: |
| 657 | +
|
| 658 | + sage: PL = algebras.FreePreLie(QQ,'@O') |
| 659 | + sage: a, b = PL.gens() |
| 660 | + sage: PL.group_product(a, b, 3, 3) |
| 661 | + B[@[]] + B[@[O[]]] + 1/2*B[@[O[], O[]]] |
| 662 | + sage: PL.group_product(a, b, 3, 10) |
| 663 | + B[@[]] + B[@[O[]]] + 1/2*B[@[O[], O[]]] + 1/6*B[@[O[], O[], O[]]] |
| 664 | + """ |
| 665 | + br = self.base_ring() |
| 666 | + return x + self.sum(self.corolla(x, y, i, N) * ~br(factorial(i)) |
| 667 | + for i in range(1, n + 1)) |
| 668 | + |
533 | 669 | def _element_constructor_(self, x):
|
534 | 670 | r"""
|
535 | 671 | Convert ``x`` into ``self``.
|
@@ -703,6 +839,34 @@ def lift(self):
|
703 | 839 | for x, cf in self.monomial_coefficients(copy=False).items()}
|
704 | 840 | return UEA.element_class(UEA, data)
|
705 | 841 |
|
| 842 | + def valuation(self): |
| 843 | + """ |
| 844 | + Return the valuation of ``self``. |
| 845 | +
|
| 846 | + EXAMPLES:: |
| 847 | +
|
| 848 | + sage: a = algebras.FreePreLie(QQ).gen(0) |
| 849 | + sage: a.valuation() |
| 850 | + 1 |
| 851 | + sage: (a*a).valuation() |
| 852 | + 2 |
| 853 | +
|
| 854 | + sage: a, b = algebras.FreePreLie(QQ,'ab').gens() |
| 855 | + sage: (a+b).valuation() |
| 856 | + 1 |
| 857 | + sage: (a*b).valuation() |
| 858 | + 2 |
| 859 | + sage: (a*b+a).valuation() |
| 860 | + 1 |
| 861 | + """ |
| 862 | + if self == self.parent().zero(): |
| 863 | + return Infinity |
| 864 | + i = 0 |
| 865 | + while True: |
| 866 | + i += 1 |
| 867 | + if self.homogeneous_component(i): |
| 868 | + return i |
| 869 | + |
706 | 870 |
|
707 | 871 | class PreLieFunctor(ConstructionFunctor):
|
708 | 872 | """
|
@@ -872,3 +1036,111 @@ def _repr_(self):
|
872 | 1036 | PreLie[x,y,z,t]
|
873 | 1037 | """
|
874 | 1038 | return "PreLie[%s]" % ','.join(self.vars)
|
| 1039 | + |
| 1040 | + |
| 1041 | +def tree_from_sortkey(ch, labels=True): |
| 1042 | + r""" |
| 1043 | + Transform a list of (valence, label) into a tree and a remainder. |
| 1044 | +
|
| 1045 | + This is like an inverse of the ``sort_key`` method. |
| 1046 | +
|
| 1047 | + INPUT: |
| 1048 | +
|
| 1049 | + - ``ch`` -- a list of pairs (integer, label)) |
| 1050 | +
|
| 1051 | + - ``labels`` -- optional (default ``True``) |
| 1052 | + 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 | + one tree ``tx``, a list of trees ``list_ty`` |
| 1104 | +
|
| 1105 | + OUTPUT: |
| 1106 | +
|
| 1107 | + trees |
| 1108 | +
|
| 1109 | + EXAMPLES:: |
| 1110 | +
|
| 1111 | + sage: from sage.combinat.free_prelie_algebra import corolla_gen |
| 1112 | + sage: a = algebras.FreePreLie(QQ).gen(0) |
| 1113 | + sage: ta = a.support()[0] |
| 1114 | + sage: list(corolla_gen(ta,[ta],False)) |
| 1115 | + [[[]]] |
| 1116 | +
|
| 1117 | + sage: a, b = algebras.FreePreLie(QQ,'ab').gens() |
| 1118 | + sage: ta = a.support()[0] |
| 1119 | + sage: tb = b.support()[0] |
| 1120 | + sage: ab = (a*b).support()[0] |
| 1121 | + sage: list(corolla_gen(ta,[tb])) |
| 1122 | + [a[b[]]] |
| 1123 | + sage: list(corolla_gen(tb,[ta,ta])) |
| 1124 | + [b[a[], a[]]] |
| 1125 | + sage: list(corolla_gen(ab,[ab,ta])) |
| 1126 | + [a[a[], b[], a[b[]]], a[a[b[]], b[a[]]], a[a[], b[a[b[]]]], |
| 1127 | + a[b[a[], a[b[]]]]] |
| 1128 | + """ |
| 1129 | + n = len(list_ty) |
| 1130 | + zx = tx.sort_key() |
| 1131 | + nx = len(zx) |
| 1132 | + liste_zy = [t.sort_key() for t in list_ty] |
| 1133 | + for list_pos in product(*[list(range(nx))] * n): |
| 1134 | + new_zx = copy(zx) |
| 1135 | + data = zip(list_pos, liste_zy) |
| 1136 | + sorted_data = sorted(data, reverse=True) |
| 1137 | + for pos_t in sorted_data: |
| 1138 | + if labels: |
| 1139 | + idx, lbl = new_zx[pos_t[0]] |
| 1140 | + new_zx = (new_zx[:pos_t[0]] + ((idx + 1, lbl),) + |
| 1141 | + pos_t[1] + new_zx[pos_t[0] + 1:]) |
| 1142 | + else: |
| 1143 | + idx = new_zx[pos_t[0]] |
| 1144 | + new_zx = (new_zx[:pos_t[0]] + (idx + 1,) + |
| 1145 | + pos_t[1] + new_zx[pos_t[0] + 1:]) |
| 1146 | + yield tree_from_sortkey(new_zx, labels=labels)[0] |
0 commit comments