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
49
50
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
76
77
78
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
80
81
82
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
84
85
86
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
110
111
112
113
114
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
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
201
202
203
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
205
206
207
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
230
231
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
237
238
239
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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
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"
    )