Source code for kauri.bck.bck

# Copyright 2025 Daniil Shmelev
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# =========================================================================

"""
The BCK Hopf algebra module
"""
from functools import cache
from ..maps import Map
from ..trees import (Tree, PlanarTree, TensorProductSum,
                     EMPTY_TREE, EMPTY_FOREST, EMPTY_FOREST_SUM)
from ..generic_algebra import forest_apply


def counit_impl(t):
    # Return 1 if t is the empty tree, otherwise 0
    return 1 if t.list_repr is None else 0

@cache
def antipode_impl(t):
    if t.list_repr is None:
        return EMPTY_FOREST_SUM # Antipode of empty tree is the empty tree
    cp = coproduct_impl(t)
    out = -t.as_forest_sum() # First term, -t
    for c, branches, subtree_ in cp: # Remaining terms
        subtree = subtree_[0] # Convert from Forest to Tree
        if subtree.equals(t) or subtree.equals(EMPTY_TREE):
            continue # We've already included the -t term at the start, so move on
        out = out - c * forest_apply(branches, antipode_impl) * subtree

    return out.simplify()

@cache
def coproduct_impl(t):
    if not isinstance(t, Tree):
        hint = " Use nck.coproduct for planar trees, or nck.map_power/nck.map_product for Map operations." if isinstance(t, PlanarTree) else ""
        raise TypeError("BCK coproduct expects a Tree, not " + str(type(t)) + "." + hint)
    # This follows the recursive definition of https://arxiv.org/pdf/hep-th/9808042
    # using B_- and B_+
    if t == Tree(None):
        return TensorProductSum(( (1, EMPTY_FOREST, EMPTY_FOREST), )) # Tree(None) @ Tree(None)
    if len(t.list_repr) == 1:
        return TensorProductSum(( (1, EMPTY_FOREST, t.as_forest()), (1, t.as_forest(), EMPTY_FOREST) )) # Tree(None) @ t + t @ Tree(None)

    root_color = t.list_repr[-1]
    branches = t.unjoin()

    cp_prod = 1
    for subtree in branches:
        cp = coproduct_impl(subtree)
        cp_prod = cp_prod * cp

    # Return t \otimes \emptyset + (id \otimes B_+)[\Delta(B_-(t))]
    out = t @ Tree(None) + TensorProductSum(tuple((c, f1, f2.join(root_color)) for c, f1, f2 in cp_prod))
    return out.simplify()

counit = Map(counit_impl)
counit.__doc__ = """
The counit :math:`\\varepsilon_{BCK}` of the BCK Hopf algebra.

:type: Map

**Example usage:**

.. kauri-exec::

    print(bck.counit(Tree(None)))  # Returns 1
    print(bck.counit(Tree([])))  # Returns 0
"""

def _safe_antipode(t):
    if not isinstance(t, Tree):
        hint = " For planar trees, use nck.antipode instead." if isinstance(t, PlanarTree) else ""
        raise TypeError("Argument to bck.antipode must be a Tree, not " + str(type(t)) + "." + hint)
    return antipode_impl(t)

antipode = Map(_safe_antipode)
antipode.__doc__ = """
The antipode :math:`S_{BCK}` of the BCK Hopf algebra.

:type: Map

**Example usage:**

.. kauri-exec::

    t = Tree([[[]],[]])
    kr.display(bck.antipode(t))
"""

[docs] def coproduct(t : Tree) -> TensorProductSum: """ The coproduct :math:`\\Delta_{BCK}` of the BCK Hopf algebra. :param t: tree :type t: Tree :rtype: TensorProductSum **Example usage:** .. kauri-exec:: t = Tree([[[]],[]]) kr.display(bck.coproduct(t)) """ if not isinstance(t, Tree): hint = " For planar trees, use nck.coproduct instead." if isinstance(t, PlanarTree) else "" raise TypeError("Argument to bck.coproduct must be a Tree, not " + str(type(t)) + "." + hint) return coproduct_impl(t)
[docs] def map_product(f : Map, g : Map) -> Map: """ Returns the product of maps in the BCK Hopf algebra, defined by .. math:: (f \\cdot g)(t) := \\mu \\circ (f \\otimes g) \\circ \\Delta_{BCK} (t) .. note:: `bck.map_product(f,g)` is equivalent to the Map operator `f * g` :param f: f :type f: Map :param g: g :type g: Map :rtype: Map **Example usage:** .. kauri-exec:: f = bck.map_product(ident, bck.antipode) print(f(Tree([[]]))) """ if not (isinstance(f, Map) and isinstance(g, Map)): raise TypeError("Arguments in bck.map_product must be of type Map, not " + str(type(f)) + " and " + str(type(g))) return f * g
[docs] def map_power(f : Map, exponent : int) -> Map: """ Returns the power of a map in the BCK Hopf algebra, where the product of functions is defined by .. math:: (f \\cdot g)(t) := \\mu \\circ (f \\otimes g) \\circ \\Delta_{BCK} (t) and negative powers are defined as :math:`f^{-n} = (f \\circ S_{BCK})^n`, where :math:`S_{BCK}` is the BCK antipode. .. note:: `bck.map_power(f, n)` is equivalent to the Map operator `f ** n` :param f: f :type f: Map :param exponent: exponent :type exponent: int **Example usage:** .. kauri-exec:: S = bck.map_power(ident, -1) # antipode print(S(Tree([[]]))) """ if not isinstance(f, Map): raise TypeError("f must be a Map, not " + str(type(f))) if not isinstance(exponent, int): raise TypeError("exponent must be an int, not " + str(type(exponent))) return f ** exponent