Skip to content

Monads

Monad definitions with units, multiplications, and monad laws.

monads

Concrete monad instances on V-enriched FinSet.

The Monad typeclass itself lives in quivers.monadic.typeclasses; this module provides two concrete monad instances together with the KleisliCategory adapter.

  • FuzzyPowersetMonad — the powerset monad over an algebra, whose Kleisli category is the V-enriched relation category.
  • FreeMonoidMonad — the free monoid monad on a finite alphabet, truncated to a maximum length.
  • KleisliCategory — wraps any Monad instance for composition.

Both concrete monads subclass Monad and implement the required fmap_obj / fmap / pure / join operations. apply is inherited from Applicative and raises when the internal-hom construction is not supplied per-instance.

FuzzyPowersetMonad

FuzzyPowersetMonad(algebra: Algebra | None = None)

Bases: Monad

The fuzzy powerset monad with a given algebra.

At the set level, T(A) = A because fuzzy subsets are represented as membership-function tensors, not as elements of a powerset. The unit η_A = identity(A) and the multiplication μ_A = identity(A).

Kleisli composition is V-enriched composition (>> on morphisms).

PARAMETER DESCRIPTION
algebra

The enrichment algebra. Defaults to PRODUCT_FUZZY.

TYPE: Algebra or None DEFAULT: None

Source code in src/quivers/monadic/monads.py
def __init__(self, algebra: Algebra | None = None) -> None:
    self._algebra = algebra if algebra is not None else PRODUCT_FUZZY

algebra property

algebra: Algebra

The enrichment algebra.

endofunctor property

endofunctor: Functor

T = Id (identity functor at set level).

unit

unit(A: SetObject) -> Morphism

η_A : A → T(A); alias for pure.

Source code in src/quivers/monadic/monads.py
def unit(self, A: SetObject) -> Morphism:
    """``η_A : A → T(A)``; alias for `pure`."""
    return self.pure(A)

multiply

multiply(A: SetObject) -> Morphism

μ_A : T(T(A)) → T(A); alias for join.

Source code in src/quivers/monadic/monads.py
def multiply(self, A: SetObject) -> Morphism:
    """``μ_A : T(T(A)) → T(A)``; alias for `join`."""
    return self.join(A)

kleisli_compose

kleisli_compose(f: Morphism, g: Morphism) -> Morphism

Kleisli composition; V-enriched composition via >>.

Source code in src/quivers/monadic/monads.py
def kleisli_compose(self, f: Morphism, g: Morphism) -> Morphism:
    """Kleisli composition; V-enriched composition via ``>>``."""
    return f >> g

FreeMonoidMonad

FreeMonoidMonad(max_length: int = 4, algebra: Algebra | None = None)

Bases: Monad

The free monoid monad, truncated to max_length.

T(A) = FreeMonoid(generators=A, max_length=max_length) = 1 + A + A² + ... + A^max_length.

  • η_A : A → A* embeds each element as a length-1 word.
  • μ_A : (A*)* → A* flattens nested words by concatenation (truncated to max_length).
PARAMETER DESCRIPTION
max_length

Maximum word length (inclusive). Defaults to 4.

TYPE: int DEFAULT: 4

algebra

The enrichment algebra. Defaults to PRODUCT_FUZZY.

TYPE: Algebra or None DEFAULT: None

Source code in src/quivers/monadic/monads.py
def __init__(self, max_length: int = 4, algebra: Algebra | None = None) -> None:
    if max_length < 1:
        raise ValueError(f"max_length must be >= 1, got {max_length}")
    self._max_length = max_length
    self._algebra = algebra if algebra is not None else PRODUCT_FUZZY

pure

pure(A: SetObject) -> Morphism

η_A : A → A* — embed elements as length-1 words.

Returns a morphism whose tensor is [0, I, 0, ..., 0] along the codomain's component-axis: zero on the empty word, identity on the length-1 component, zero elsewhere.

Source code in src/quivers/monadic/monads.py
def pure(self, A: SetObject) -> Morphism:
    """``η_A : A → A*`` — embed elements as length-1 words.

    Returns a morphism whose tensor is ``[0, I, 0, ..., 0]`` along
    the codomain's component-axis: zero on the empty word, identity
    on the length-1 component, zero elsewhere.
    """
    import torch

    if not isinstance(A, FinSet):
        raise TypeError(
            f"FreeMonoidMonad.pure requires a FinSet, got {type(A).__name__}"
        )
    ta = self.fmap_obj(A)
    n = A.cardinality
    total = ta.size  # type: ignore[attr-defined]
    # offset of the length-1 component
    start, _ = ta.component_range(1)  # type: ignore[attr-defined]
    data = torch.zeros((n, total))
    for i in range(n):
        data[i, start + i] = 1.0
    return observed(A, ta, data, algebra=self._algebra)

join

join(A: SetObject) -> Morphism

μ_A : (A*)* → A* — flatten nested words by concatenation.

The flattened-result indexing follows the canonical word-encoding of FreeMonoid; flattenings whose total length exceeds max_length are dropped.

Source code in src/quivers/monadic/monads.py
def join(self, A: SetObject) -> Morphism:
    """``μ_A : (A*)* → A*`` — flatten nested words by concatenation.

    The flattened-result indexing follows the canonical word-encoding
    of `FreeMonoid`; flattenings whose total length exceeds
    ``max_length`` are dropped.
    """
    import torch

    if not isinstance(A, FinSet):
        raise TypeError(
            f"FreeMonoidMonad.join requires a FinSet, got {type(A).__name__}"
        )
    ta = self.fmap_obj(A)  # FreeMonoid(A, max_length)
    # The outer free monoid is over an alphabet whose elements are
    # the indices of the inner free monoid; that alphabet is a
    # FinSet of cardinality ta.size.
    outer_alphabet = FinSet(
        name=f"{A.name}*",
        cardinality=ta.size,  # type: ignore[attr-defined]
    )
    tta = FreeMonoid(generators=outer_alphabet, max_length=self._max_length)
    outer_size = tta.size  # type: ignore[attr-defined]
    inner_size = ta.size  # type: ignore[attr-defined]
    data = torch.zeros((outer_size, inner_size))
    for k in range(outer_size):
        outer_word = tta.decode(k)  # type: ignore[attr-defined]
        # Each entry of outer_word is an index into ta; decode it
        # to obtain the inner A-word, then concatenate.
        concatenated: list[int] = []
        for inner_idx in outer_word:
            inner_word = ta.decode(inner_idx)  # type: ignore[attr-defined]
            concatenated.extend(inner_word)
        if len(concatenated) > self._max_length:
            continue
        flat = ta.encode(tuple(concatenated))  # type: ignore[attr-defined]
        data[k, flat] = 1.0
    return observed(tta, ta, data, algebra=self._algebra)

unit

unit(A: SetObject) -> Morphism

Alias for pure.

Source code in src/quivers/monadic/monads.py
def unit(self, A: SetObject) -> Morphism:
    """Alias for `pure`."""
    return self.pure(A)

multiply

multiply(A: SetObject) -> Morphism

Alias for join.

Source code in src/quivers/monadic/monads.py
def multiply(self, A: SetObject) -> Morphism:
    """Alias for `join`."""
    return self.join(A)

KleisliCategory

KleisliCategory(monad: Monad)

The Kleisli category of a monad.

Objects are the same as the base category. Morphisms A → B in the Kleisli category are morphisms A → T(B) in the base category. Composition uses the underlying monad's Monad.join, falling through to a closed-form kleisli_compose method on the monad when available.

PARAMETER DESCRIPTION
monad

The underlying monad instance.

TYPE: Monad

Source code in src/quivers/monadic/monads.py
def __init__(self, monad: Monad) -> None:
    self._monad = monad

identity

identity(obj: SetObject) -> Morphism

Kleisli identity: η_A : A → T(A).

Source code in src/quivers/monadic/monads.py
def identity(self, obj: SetObject) -> Morphism:
    """Kleisli identity: ``η_A : A → T(A)``."""
    return self._monad.pure(obj)

compose

compose(f: Morphism, g: Morphism) -> Morphism

Kleisli composition.

For f : A → T(B) and g : B → T(C), returns A → T(C) via the monad's bind / join construction.

Source code in src/quivers/monadic/monads.py
def compose(self, f: Morphism, g: Morphism) -> Morphism:
    """Kleisli composition.

    For ``f : A → T(B)`` and ``g : B → T(C)``, returns
    ``A → T(C)`` via the monad's bind / join construction.
    """
    # Defer to a closed-form kleisli_compose when the monad ships
    # one; otherwise the generic join-based construction requires
    # an internal-hom representation supplied per instance.
    kc = getattr(self._monad, "kleisli_compose", None)
    if callable(kc):
        return kc(f, g)
    raise NotImplementedError(
        f"KleisliCategory.compose: {type(self._monad).__name__} "
        "exposes no closed-form kleisli composition; supply one "
        "via the bridges in quivers.monadic.bridges"
    )