Skip to content

Transformations

Change-of-base transformations between algebras: pointwise algebra homomorphisms, shape-aware morphism transformations, and the sequences compose_trans builds from them. Each is consumed by Morphism.change_base.

algebra_morphisms

Algebra homomorphisms and change-of-base for V-enriched morphisms.

A morphism in quivers.core.morphisms is V-enriched: its codomain is :math:\mathrm{Hom}_V(A, B) for the active algebra V. Two morphisms over different algebras (V and W) do not directly compose — Morphism.__rshift__ raises incompatible algebras. The standard categorical way to bridge them is a algebra homomorphism (a lax monoidal poset functor) :math:\varphi : V \to W mediating the change of base. There is then a 2-functor :math:(-) \otimes_\varphi W : V\text{-}\mathbf{Cat} \to W\text{-}\mathbf{Cat} that base-changes objects and an induced natural transformation on morphisms.

This module ships:

  • AlgebraHomomorphism — the abstract base. A homomorphism carries a source algebra, a target algebra, and a function apply(t : Tensor) -> Tensor that maps a tensor whose entries live in the source's lattice to a tensor whose entries live in the target's lattice. Subclasses implement apply for a specific pair.

  • Embedding — the inclusion of a sub-algebra into a super-algebra (e.g. Boolean ↪ ProductFuzzyAlgebra).

  • Expectation — Markov-to-ProductFuzzyAlgebra (softmax ↦ fuzzy membership).

  • LogProb — ProductFuzzyAlgebra-to-LogProb (log(p)).

  • MaxPlus — ProductFuzzyAlgebra-to-Viterbi (max-plus tropical lift).

  • Threshold — ProductFuzzyAlgebra-to-Boolean (discretize at a threshold).

  • MaterialImplication — ProductFuzzyAlgebra-to-Godel (Heyting implication semantics).

  • IdentityHom — a no-op homomorphism from an algebra to itself, useful as the unit of homomorphism composition.

  • A registry HOMOMORPHISM_REGISTRY keyed by (source.name, target.name) so the compiler / user code can look up the canonical homomorphism between two algebras.

The categorical denotation of f.change_base(φ) for f : A → B in V is the V-Cat morphism φ ∘ f : A → B in W with tensor φ.apply(f.tensor) and lattice values in W. Composition then proceeds in W using W's tensor / join operations.

This module is intentionally backend-agnostic: every homomorphism's apply operates on a torch.Tensor without touching PyTorch's autograd or module infrastructure, so a future numpy / JAX backend can replace the runtime without touching the categorical hierarchy.

AlgebraHomomorphism

Bases: ABC

A lax monoidal poset functor between two algebras.

Concretely, a homomorphism :math:\varphi : V \to W satisfies:

  • φ(a ⊗_V b) ≤ φ(a) ⊗_W φ(b) (lax with respect to the monoidal tensor).
  • φ(⋁_i a_i) ≤ ⋁_i φ(a_i) (lax with respect to joins).
  • φ(I_V) ≤ I_W (lax with respect to the monoidal unit).

The "lax" qualifier allows for embeddings that preserve order but compress structure (a threshold is lax but not strict). Strictness is not required for change-of-base to be categorically valid; only the order-preservation is.

source abstractmethod property

source: Algebra

The source algebra V.

target abstractmethod property

target: Algebra

The target algebra W.

apply abstractmethod

apply(t: Tensor) -> Tensor

Map a tensor with entries in source.L to a tensor with entries in target.L.

The shape is preserved; only the per-entry lattice changes.

Source code in src/quivers/core/algebra_morphisms.py
@abstractmethod
def apply(self, t: torch.Tensor) -> torch.Tensor:
    """Map a tensor with entries in ``source.L`` to a tensor
    with entries in ``target.L``.

    The shape is preserved; only the per-entry lattice changes.
    """

IdentityHom

IdentityHom(q: Algebra)

Bases: AlgebraHomomorphism

The identity homomorphism id_V : V → V.

Useful as the unit of composition; f.change_base(IdentityHom(V)) leaves the morphism in V unchanged.

Source code in src/quivers/core/algebra_morphisms.py
def __init__(self, q: Algebra) -> None:
    self._q = q

Embedding

Embedding(source: Algebra, target: Algebra)

Bases: AlgebraHomomorphism

The inclusion of a sub-algebra into a super-algebra.

The canonical example is Boolean ↪ ProductFuzzyAlgebra: a boolean tensor with entries in {0, 1} embeds as a product-fuzzy tensor with entries in [0, 1] whose support is the boolean's truth assignment.

Embeddings preserve every algebra operation up to the embedding inclusion; they are strict homomorphisms.

Source code in src/quivers/core/algebra_morphisms.py
def __init__(self, source: Algebra, target: Algebra) -> None:
    self._source = source
    self._target = target

Expectation

Expectation()

Bases: AlgebraHomomorphism

Markov → ProductFuzzyAlgebra via softmax to fuzzy membership.

A Markov kernel k : A → B has rows that sum to 1 (a probability distribution over B per a∈A). Reading the per-entry probability as a fuzzy membership of (a, b) in the relation gives a ProductFuzzyAlgebra morphism.

Categorically this is the change of base induced by the Δ natural transformation that erases the normalisation constraint — the resulting fuzzy morphism is no longer row-stochastic but its entries still lie in [0, 1].

Source code in src/quivers/core/algebra_morphisms.py
def __init__(self) -> None:
    self._source = MARKOV
    self._target = PRODUCT_FUZZY

LogProb

LogProb()

Bases: AlgebraHomomorphism

ProductFuzzyAlgebra → LogProb via log.

Maps entries in (0, 1] to (-∞, 0] for log-space computation. Entries that are exactly 0 in the source go to -inf. In practice the input is clamped to [ε, 1] first to avoid numerical blow-up.

Source code in src/quivers/core/algebra_morphisms.py
def __init__(self) -> None:
    self._source = PRODUCT_FUZZY
    self._target = LOG_PROB

MaxPlus

MaxPlus()

Bases: AlgebraHomomorphism

ProductFuzzyAlgebra → MaxPlus via log.

The max-plus tropical algebra on the reals has tensor = addition and join = max. A ProductFuzzyAlgebra morphism transports via log so the product t-norm becomes addition in log-space; subsequent joins (which were noisy-OR in ProductFuzzyAlgebra) get replaced by max in Viterbi-style Viterbi-MAP aggregations.

The two ProductFuzzyAlgebra → log-target homomorphisms are different category-theoretically — LogProb carries the join structure to logsumexp, MaxPlus carries it to max-plus — even though they share the same per-entry mapping log.

Source code in src/quivers/core/algebra_morphisms.py
def __init__(self) -> None:
    self._source = PRODUCT_FUZZY
    self._target = MAX_PLUS

Threshold

Threshold(tau: float = 0.5)

Bases: AlgebraHomomorphism

ProductFuzzyAlgebra → Boolean via thresholding.

Entries above tau map to True; entries at or below tau map to False. This is a lax homomorphism: it preserves order (a ≤ b ⟹ threshold(a) ≤ threshold(b)) but compresses information (every value in (tau, 1] collapses to True).

PARAMETER DESCRIPTION
tau

Threshold value in [0, 1].

TYPE: float DEFAULT: 0.5

Source code in src/quivers/core/algebra_morphisms.py
def __init__(self, tau: float = 0.5) -> None:
    if not 0.0 <= tau <= 1.0:
        raise ValueError(f"Threshold: tau must be in [0, 1]; got {tau}")
    self._tau = tau
    self._source = PRODUCT_FUZZY
    self._target = BOOLEAN

MaterialImplication

MaterialImplication()

Bases: AlgebraHomomorphism

ProductFuzzyAlgebra → Godel via a * b + (1 - a) reading.

Reads each ProductFuzzyAlgebra entry p as a probability that the implication p → q holds — the Heyting-implication- style transport that respects the Godel algebra's monoid.

The mapping is entry ↦ entry-preserving on the entry value but changes the composition semantics downstream: subsequent >> in the Godel algebra uses min / Godel-implication, not product / noisy-OR.

Source code in src/quivers/core/algebra_morphisms.py
def __init__(self) -> None:
    self._source = PRODUCT_FUZZY
    self._target = GODEL

ProbabilityClamp

ProbabilityClamp()

Bases: AlgebraHomomorphism

Real → Probability via clamping to the unit interval.

Coerces a real-valued tensor into a probability tensor by clamping entries to [0, 1]. Lax — destroys information outside the unit interval — but preserves the entry-wise order on the survivors.

Source code in src/quivers/core/algebra_morphisms.py
def __init__(self) -> None:
    self._source = REAL
    self._target = PROBABILITY

CountingFromReal

CountingFromReal()

Bases: AlgebraHomomorphism

Real → Counting via floor-and-clamp-to-non-negative.

Coerces a real-valued tensor into a non-negative integer counting tensor by flooring and clamping at zero. Information-destroying; inverse is CountingToReal.

Source code in src/quivers/core/algebra_morphisms.py
def __init__(self) -> None:
    self._source = REAL
    self._target = COUNTING

ProbabilityToReal

ProbabilityToReal()

Bases: AlgebraHomomorphism

Probability → Real (sub-algebra inclusion).

Entries already lie in [0, 1] ⊂ ℝ; the inclusion is strict (preserves every operation).

Source code in src/quivers/core/algebra_morphisms.py
def __init__(self) -> None:
    self._source = PROBABILITY
    self._target = REAL

CountingToReal

CountingToReal()

Bases: AlgebraHomomorphism

Counting → Real (sub-algebra inclusion).

Non-negative integers embed canonically in the reals; this is the strict inclusion homomorphism.

Source code in src/quivers/core/algebra_morphisms.py
def __init__(self) -> None:
    self._source = COUNTING
    self._target = REAL

threshold

threshold(tau: float = 0.5) -> Threshold

Build a Threshold homomorphism at the given threshold value.

PARAMETER DESCRIPTION
tau

The threshold; entries strictly above it map to true.

TYPE: float DEFAULT: 0.5

RETURNS DESCRIPTION
Threshold

The ProductFuzzy-to-Boolean homomorphism at tau.

Source code in src/quivers/core/algebra_morphisms.py
def threshold(tau: float = 0.5) -> Threshold:
    """Build a `Threshold` homomorphism at the given threshold value.

    Parameters
    ----------
    tau : float
        The threshold; entries strictly above it map to true.

    Returns
    -------
    Threshold
        The ProductFuzzy-to-Boolean homomorphism at ``tau``.
    """
    return Threshold(tau)

embedding

embedding(source: Algebra, target: Algebra) -> Embedding

Build an Embedding homomorphism, a sub-algebra inclusion.

PARAMETER DESCRIPTION
source

The sub-algebra.

TYPE: Algebra

target

The super-algebra that source includes into.

TYPE: Algebra

RETURNS DESCRIPTION
Embedding

The inclusion source → target.

Source code in src/quivers/core/algebra_morphisms.py
def embedding(source: Algebra, target: Algebra) -> Embedding:
    """Build an `Embedding` homomorphism, a sub-algebra inclusion.

    Parameters
    ----------
    source : Algebra
        The sub-algebra.
    target : Algebra
        The super-algebra that ``source`` includes into.

    Returns
    -------
    Embedding
        The inclusion ``source → target``.
    """
    return Embedding(source, target)

lookup_homomorphism

lookup_homomorphism(source: Algebra, target: Algebra) -> AlgebraHomomorphism | None

Look up the canonical homomorphism source → target.

Identity is always available: algebras of one type yield IdentityHom(source).

PARAMETER DESCRIPTION
source

The algebra to map from.

TYPE: Algebra

target

The algebra to map to.

TYPE: Algebra

RETURNS DESCRIPTION
AlgebraHomomorphism or None

The registered bridge from HOMOMORPHISM_REGISTRY, or None when no canonical bridge is known.

Source code in src/quivers/core/algebra_morphisms.py
def lookup_homomorphism(source: Algebra, target: Algebra) -> AlgebraHomomorphism | None:
    """Look up the canonical homomorphism ``source → target``.

    Identity is always available: algebras of one type yield
    ``IdentityHom(source)``.

    Parameters
    ----------
    source : Algebra
        The algebra to map from.
    target : Algebra
        The algebra to map to.

    Returns
    -------
    AlgebraHomomorphism or None
        The registered bridge from `HOMOMORPHISM_REGISTRY`, or
        ``None`` when no canonical bridge is known.
    """
    if type(source) is type(target):
        return IdentityHom(source)
    return HOMOMORPHISM_REGISTRY.get((source.name, target.name))

morphism_transformations

Functorial change-of-base for non-pointwise morphism transformations.

A quivers.core.algebra_morphisms.AlgebraHomomorphism acts pointwise on tensor entries (log, threshold, max-plus lift). That handles a useful but narrow class of change-of-base moves. The broader class — functors V-Cat -> W-Cat whose action on a morphism's tensor is shape-aware rather than strictly pointwise — covers softmax-style normalizations, Bayes-inversion under a prior, L1 / L2 / spectral row normalizations, top-k truncation, and Sinkhorn balancing.

This module ships the abstract base MorphismTransformation and four concrete subclasses (Softmax, L1Normalize, L2Normalize, BayesInvert) that cover the common practical needs. Morphism.change_base accepts either a AlgebraHomomorphism (pointwise) or a MorphismTransformation (shape-aware) and dispatches accordingly.

Categorically, a MorphismTransformation is a (possibly lax) 2-functor between V-Cat and W-Cat that need not factor through a algebra homomorphism on tensor entries — the action operates on whole rows / columns / matrices, and may carry parameter data (BayesInvert carries a prior).

MorphismTransformation

Bases: ABC

A shape-aware change-of-base transformation.

Concrete subclasses act on a morphism's whole tensor (not entry-by-entry), optionally consuming an explicit axis specification or external parameters (e.g. a prior). The source algebra is the domain of the action; the target algebra is the resulting morphism's algebra.

The base contract is:

morphism :: V-Cat(A, B)  -->  V-Cat(A', B')

where (A', B') is determined by the transformation (often the same as (A, B) for normalizations; swapped for Bayes inversion).

source abstractmethod property

source: Algebra

The source algebra V.

target abstractmethod property

target: Algebra

The target algebra W.

apply abstractmethod

apply(tensor: Tensor, morphism) -> Tensor

Transform morphism.tensor into a tensor over the target algebra.

PARAMETER DESCRIPTION
tensor

The morphism's tensor at the current algebra.

TYPE: Tensor

morphism

The full morphism, exposed so the transformation can consult domain / codomain shape and pick the right axis.

TYPE: Morphism

Source code in src/quivers/core/morphism_transformations.py
@abstractmethod
def apply(self, tensor: torch.Tensor, morphism) -> torch.Tensor:
    """Transform ``morphism.tensor`` into a tensor over the target
    algebra.

    Parameters
    ----------
    tensor : torch.Tensor
        The morphism's tensor at the current algebra.
    morphism : Morphism
        The full morphism, exposed so the transformation can
        consult domain / codomain shape and pick the right axis.
    """

new_domain

new_domain(morphism) -> SetObject

The transformed morphism's domain. Default: unchanged.

Source code in src/quivers/core/morphism_transformations.py
def new_domain(self, morphism) -> SetObject:
    """The transformed morphism's domain. Default: unchanged."""
    return morphism.domain

new_codomain

new_codomain(morphism) -> SetObject

The transformed morphism's codomain. Default: unchanged.

Source code in src/quivers/core/morphism_transformations.py
def new_codomain(self, morphism) -> SetObject:
    """The transformed morphism's codomain. Default: unchanged."""
    return morphism.codomain

Softmax

Softmax(axis_object: SetObject, source: Algebra = PRODUCT_FUZZY)

Bases: MorphismTransformation

Softmax-normalize along an axis, producing a Markov-style kernel.

Maps a ProductFuzzyAlgebra (or Real) tensor to a row-stochastic Markov kernel by applying softmax along the axis indexed by axis_object. The output's entries lie in (0, 1) and sum to 1 along that axis.

PARAMETER DESCRIPTION
axis_object

The object whose axis the softmax is taken over.

TYPE: SetObject

source

The morphism's algebra before softmax. Default PRODUCT_FUZZY.

TYPE: Algebra DEFAULT: PRODUCT_FUZZY

Source code in src/quivers/core/morphism_transformations.py
def __init__(
    self,
    axis_object: SetObject,
    source: Algebra = PRODUCT_FUZZY,
) -> None:

    self._axis_object = axis_object
    self._source = source
    self._target = MARKOV

L1Normalize

L1Normalize(axis_object: SetObject, source: Algebra = REAL)

Bases: MorphismTransformation

L1-normalize along an axis: divide each entry by the row sum.

Maps a non-negative real tensor to one whose entries sum to 1 along the given axis. The output is row-stochastic on that axis; semantically identical to a Markov kernel.

PARAMETER DESCRIPTION
axis_object

The object whose axis is normalized.

TYPE: SetObject

source

Default REAL.

TYPE: Algebra DEFAULT: REAL

Source code in src/quivers/core/morphism_transformations.py
def __init__(
    self,
    axis_object: SetObject,
    source: Algebra = REAL,
) -> None:

    self._axis_object = axis_object
    self._source = source
    self._target = MARKOV

L2Normalize

L2Normalize(axis_object: SetObject, source: Algebra = REAL)

Bases: MorphismTransformation

L2-normalize along an axis: divide each entry by the row norm.

Maps a real-valued tensor to one whose entries lie on the unit L2 sphere along the given axis. The result is still in the source algebra (Real), with rows rescaled to unit length.

PARAMETER DESCRIPTION
axis_object

The object whose axis is normalized.

TYPE: SetObject

source

Default REAL. The target equals the source.

TYPE: Algebra DEFAULT: REAL

Source code in src/quivers/core/morphism_transformations.py
def __init__(
    self,
    axis_object: SetObject,
    source: Algebra = REAL,
) -> None:
    self._axis_object = axis_object
    self._source = source
    self._target = source

BayesInvert

BayesInvert(prior: Tensor)

Bases: MorphismTransformation

Bayes-invert a Markov kernel under a prior.

Given a Markov kernel f : A -> B and a prior pi : 1 -> A, the Bayes-inverse f^{-1}_pi : B -> A is the kernel satisfying

f^{-1}_pi(a | b) = f(b | a) * pi(a) / sum_a' f(b | a') * pi(a')

Mathematically this is the disintegration of the joint distribution along the second margin. The domain and codomain swap roles in the result.

PARAMETER DESCRIPTION
prior

Prior probabilities over the source's first object. Shape (|A|,); must sum to 1.

TYPE: Tensor

Source code in src/quivers/core/morphism_transformations.py
def __init__(self, prior: torch.Tensor) -> None:

    if prior.dim() != 1:
        raise ValueError(f"BayesInvert: prior must be 1-D; got {prior.dim()}-D")
    if not torch.isclose(prior.sum(), torch.ones(())):
        raise ValueError(
            f"BayesInvert: prior must sum to 1; got sum={float(prior.sum()):.4f}"
        )
    if (prior < 0).any():
        raise ValueError("BayesInvert: prior entries must be non-negative")
    self._prior = prior.clone()
    self._source = MARKOV
    self._target = MARKOV

softmax

softmax(axis_object: SetObject) -> Softmax

Build a Softmax transformation along axis_object.

PARAMETER DESCRIPTION
axis_object

The object whose axis the softmax is taken over.

TYPE: SetObject

RETURNS DESCRIPTION
Softmax

The ProductFuzzy-to-Markov transformation.

Source code in src/quivers/core/morphism_transformations.py
def softmax(axis_object: SetObject) -> Softmax:
    """Build a `Softmax` transformation along ``axis_object``.

    Parameters
    ----------
    axis_object : SetObject
        The object whose axis the softmax is taken over.

    Returns
    -------
    Softmax
        The ProductFuzzy-to-Markov transformation.
    """
    return Softmax(axis_object)

l1_normalize

l1_normalize(axis_object: SetObject) -> L1Normalize

Build an L1Normalize transformation along axis_object.

PARAMETER DESCRIPTION
axis_object

The object whose axis is normalized.

TYPE: SetObject

RETURNS DESCRIPTION
L1Normalize

The Real-to-Markov transformation.

Source code in src/quivers/core/morphism_transformations.py
def l1_normalize(axis_object: SetObject) -> L1Normalize:
    """Build an `L1Normalize` transformation along ``axis_object``.

    Parameters
    ----------
    axis_object : SetObject
        The object whose axis is normalized.

    Returns
    -------
    L1Normalize
        The Real-to-Markov transformation.
    """
    return L1Normalize(axis_object)

l2_normalize

l2_normalize(axis_object: SetObject) -> L2Normalize

Build an L2Normalize transformation along axis_object.

PARAMETER DESCRIPTION
axis_object

The object whose axis is normalized.

TYPE: SetObject

RETURNS DESCRIPTION
L2Normalize

The Real-to-Real transformation.

Source code in src/quivers/core/morphism_transformations.py
def l2_normalize(axis_object: SetObject) -> L2Normalize:
    """Build an `L2Normalize` transformation along ``axis_object``.

    Parameters
    ----------
    axis_object : SetObject
        The object whose axis is normalized.

    Returns
    -------
    L2Normalize
        The Real-to-Real transformation.
    """
    return L2Normalize(axis_object)

bayes_invert

bayes_invert(prior) -> BayesInvert

Build a BayesInvert transformation from a prior.

prior may be a 1-D torch.Tensor (used directly) or any object that exposes a .tensor attribute (the morphism convention used throughout the V-Cat layer). Duck typing on .tensor avoids a circular import between quivers.core.morphisms and this module. The morphism form is what the DSL feeds in when the user writes change_base(bayes_invert(prior_morph)).

PARAMETER DESCRIPTION
prior

The prior over the kernel's domain, as a tensor or as a morphism whose tensor holds it. A prior that does not sum to 1 is clamped to be non-negative and renormalized.

TYPE: Tensor or Morphism

RETURNS DESCRIPTION
BayesInvert

The Bayes-inversion transformation under prior.

RAISES DESCRIPTION
TypeError

If prior is neither a tensor nor carries a tensor.

ValueError

If prior sums to zero.

Source code in src/quivers/core/morphism_transformations.py
def bayes_invert(prior) -> BayesInvert:
    """Build a `BayesInvert` transformation from a prior.

    ``prior`` may be a 1-D `torch.Tensor` (used directly)
    or any object that exposes a ``.tensor`` attribute (the
    morphism convention used throughout the V-Cat layer). Duck
    typing on ``.tensor`` avoids a circular import between
    [`quivers.core.morphisms`][quivers.core.morphisms] and this module. The morphism
    form is what the DSL feeds in when the user writes
    ``change_base(bayes_invert(prior_morph))``.

    Parameters
    ----------
    prior : torch.Tensor or Morphism
        The prior over the kernel's domain, as a tensor or as a
        morphism whose ``tensor`` holds it. A prior that does not sum
        to 1 is clamped to be non-negative and renormalized.

    Returns
    -------
    BayesInvert
        The Bayes-inversion transformation under ``prior``.

    Raises
    ------
    TypeError
        If ``prior`` is neither a tensor nor carries a ``tensor``.
    ValueError
        If ``prior`` sums to zero.
    """
    if isinstance(prior, torch.Tensor):
        tensor = prior
    elif hasattr(prior, "tensor"):
        tensor = prior.tensor.detach().clone()
    else:
        raise TypeError(
            f"bayes_invert: expected Tensor or .tensor-bearing "
            f"morphism prior; got {type(prior).__name__}"
        )
    flat = tensor.flatten()
    total = flat.sum()
    if total.abs() < 1e-12:
        raise ValueError("bayes_invert: prior sums to zero; cannot normalize")
    if not torch.isclose(total, torch.tensor(1.0), atol=1e-5):
        flat = flat.clamp(min=0.0)
        flat = flat / flat.sum().clamp(min=1e-12)
    return BayesInvert(flat)

trans

First-class transformation composition.

A transformation in quivers is either a quivers.core.algebra_morphisms.AlgebraHomomorphism (pointwise, lax monoidal) or a quivers.core.morphism_transformations.MorphismTransformation (shape-aware). Both expose a source and target algebra and an apply that consumes a morphism's tensor.

This module gives them a composition operation: compose_trans(t1, t2, ...) returns a TransSeq value that applies the steps in order when handed to Morphism.change_base. Adjacent target and source are type-checked at compose time. Nested TransSeq are flattened so the resulting steps tuple is always a single flat sequence of base transformations.

This is the Python-side surface for the DSL's t1 >>> t2 operator: in the .qvr surface, let pipe = softmax(B) >>> expectation builds the same value that compose_trans(softmax(B), EXPECTATION) builds here.

TransSeq

TransSeq(steps: tuple[TransValue, ...])

Sequence of transformations applied left-to-right.

Constructed by compose_trans; consumed by Morphism.change_base. Each adjacent target / source boundary in steps is verified at construction time, so apply does not have to revalidate.

A TransSeq itself exposes the same source / target / name interface as a single transformation, so it can be fed into further compose_trans calls and the nesting is flattened.

Source code in src/quivers/core/trans.py
def __init__(self, steps: tuple[TransValue, ...]) -> None:
    if len(steps) < 2:
        raise ValueError(
            "TransSeq: requires at least two steps; a "
            "single-step trans value is the base "
            "transformation itself, not a sequence"
        )
    for i in range(len(steps) - 1):
        tgt = steps[i].target
        src = steps[i + 1].source
        if type(tgt) is not type(src):
            raise TypeError(
                f"compose_trans: target of step {i} "
                f"({tgt.name!r}) does not match source of "
                f"step {i + 1} ({src.name!r})"
            )
    self._steps: tuple[TransValue, ...] = steps
    self._source = steps[0].source
    self._target = steps[-1].target

compose_trans

compose_trans(*steps: TransValue | TransSeq) -> TransSeq

Compose two or more transformations into a single sequence.

Each argument is either a base transformation (a AlgebraHomomorphism or MorphismTransformation) or another TransSeq whose steps are inlined. The resulting sequence is flat; nested compositions never appear in TransSeq.steps.

PARAMETER DESCRIPTION
*steps

Two or more transformations.

TYPE: TransValue or TransSeq DEFAULT: ()

RETURNS DESCRIPTION
TransSeq

The composed sequence with source = steps[0].source and target = steps[-1].target.

RAISES DESCRIPTION
ValueError

If fewer than two steps are supplied.

TypeError

If two adjacent steps' target / source algebras differ.

Source code in src/quivers/core/trans.py
def compose_trans(*steps: TransValue | TransSeq) -> TransSeq:
    """Compose two or more transformations into a single sequence.

    Each argument is either a base transformation (a
    ``AlgebraHomomorphism`` or ``MorphismTransformation``) or
    another `TransSeq` whose steps are inlined.  The
    resulting sequence is flat; nested compositions never appear
    in `TransSeq.steps`.

    Parameters
    ----------
    *steps : TransValue or TransSeq
        Two or more transformations.

    Returns
    -------
    TransSeq
        The composed sequence with ``source = steps[0].source``
        and ``target = steps[-1].target``.

    Raises
    ------
    ValueError
        If fewer than two steps are supplied.
    TypeError
        If two adjacent steps' target / source algebras differ.
    """
    if len(steps) < 2:
        raise ValueError("compose_trans: requires at least two transformations")
    flat: list[TransValue] = []
    for step in steps:
        if isinstance(step, TransSeq):
            flat.extend(step.steps)
        else:
            flat.append(step)
    return TransSeq(tuple(flat))

The homomorphism singletons, their registry, and the TransValue alias are documented on the core overview.