Source code for uconf.algebraic.pullback_algebra
from uconf.algebraic.algebra import OperadAlgebra
from uconf.core.morphism import OperadMorphism
from sage.all import cached_method
from typing import Any
[docs]
class PullbackAlgebra(OperadAlgebra):
"""Pull back an algebra along an operad morphism.
Given a ``Q``-algebra ``algebra`` and a morphism ``f: P → Q``,
the pullback is the ``P``-algebra with the same underlying module
and structure map ``γ^P(p; a_1,…,a_n) = γ^Q(f(p); a_1,…,a_n)``.
Parameters
----------
morphism : OperadMorphism
A morphism ``f: P → Q``.
algebra : OperadAlgebra
A ``Q``-algebra.
"""
def __init__(self, morphism: OperadMorphism, algebra: Any):
self.morphism = morphism
self.algebra = algebra
self.module = algebra.module
self.operad_cls = morphism.source
# Cache morphism results: the morphism f(p) depends only on p,
# not on the algebra elements. We cache by the frozenset of
# (key, coeff) pairs, which is hashable and identifies the element.
self._morphism_cache: dict = {}
@cached_method
def _act_on_basis_inputs(self, p_terms: tuple, algebra_input_keys: tuple):
"""Apply the pullback action to basis inputs and cache the result."""
arity = len(algebra_input_keys)
base_ring = self.module.base_ring()
p_element = self.operad_cls(arity, base_ring)._from_dict(dict(p_terms), remove_zeros=True)
q_element = self._morphism_cache.get(p_terms)
if q_element is None:
q_element = self.morphism(p_element)
self._morphism_cache[p_terms] = q_element
act_on_basis_inputs = getattr(self.algebra, "_act_on_basis_inputs", None)
if callable(act_on_basis_inputs):
return act_on_basis_inputs(tuple(q_element), tuple(algebra_input_keys))
return self.algebra.act(q_element, [self.module.term(key) for key in algebra_input_keys])
[docs]
def act(self, p_element: Any, algebra_elements: Any) -> Any:
"""Apply the pullback structure map ``γ^Q(f(p); a_1, …, a_n)``."""
# Cache key: tuple of (basis_key, coeff) pairs, which is hashable
cache_key = tuple(p_element)
cached = self._morphism_cache.get(cache_key)
if cached is not None:
q_element = cached
else:
q_element = self.morphism(p_element)
self._morphism_cache[cache_key] = q_element
return self.algebra.act(q_element, algebra_elements)
[docs]
def boundary(self, a: Any) -> Any:
"""Apply the differential of the underlying module."""
return self.algebra.boundary(a)