Morphisms

Base morphism classes, composition operations, and morphism utilities for all categorical structures.

morphisms

Morphism hierarchy: V-enriched relations between finite sets.

A morphism from domain D to codomain C is represented as a tensor of shape (D.shape, C.shape) with entries in the lattice L of a quantale. Composition uses the quantale's operations (join over tensor product).

The hierarchy:

Morphism (abstract)
├── ObservedMorphism    — fixed tensor (not learned)
├── LatentMorphism      — nn.Parameter with sigmoid constraint
├── ComposedMorphism    — f >> g (V-enriched composition)
├── ProductMorphism     — f @ g (tensor / parallel product)
├── MarginalizedMorphism — contract codomain dims via join
├── FunctorMorphism     — lazy image of a morphism under a functor
└── RepeatMorphism      — runtime-variable iterated composition (T^n)

Morphism

Morphism(domain: SetObject, codomain: SetObject, quantale: Quantale | None = None)

Bases: ABC

Abstract base for morphisms between finite sets.

Subclasses must implement tensor (returns the materialized tensor with values in the quantale's lattice) and module (returns the nn.Module tree for parameter collection).

PARAMETER DESCRIPTION
domain

Source object.

TYPE: SetObject

codomain

Target object.

TYPE: SetObject

quantale

The enrichment algebra. Defaults to PRODUCT_FUZZY.

TYPE: Quantale or None DEFAULT: None

Source code in src/quivers/core/morphisms.py
48
49
50
51
52
53
def __init__(
    self, domain: SetObject, codomain: SetObject, quantale: Quantale | None = None
) -> None:
    self._domain = domain
    self._codomain = codomain
    self._quantale = quantale if quantale is not None else PRODUCT_FUZZY

domain property

domain: SetObject

Source object.

codomain property

codomain: SetObject

Target object.

quantale property

quantale: Quantale

The enrichment algebra for this morphism.

tensor_shape property

tensor_shape: tuple[int, ...]

Expected shape of the materialized tensor.

tensor abstractmethod property

tensor: Tensor

Materialize the morphism as a tensor with values in L.

module abstractmethod

module() -> Module

Return an nn.Module wrapping all learnable parameters.

Source code in src/quivers/core/morphisms.py
81
82
83
84
@abstractmethod
def module(self) -> nn.Module:
    """Return an nn.Module wrapping all learnable parameters."""
    ...

__rshift__

__rshift__(other: Morphism) -> ComposedMorphism

V-enriched composition: self >> other.

Composes self: A -> B with other: B -> C to yield a morphism A -> C, contracting over B using the quantale.

PARAMETER DESCRIPTION
other

Right morphism whose domain must match self's codomain.

TYPE: Morphism

RETURNS DESCRIPTION
ComposedMorphism

The composed morphism.

Source code in src/quivers/core/morphisms.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def __rshift__(self, other: Morphism) -> ComposedMorphism:
    """V-enriched composition: self >> other.

    Composes self: A -> B with other: B -> C to yield
    a morphism A -> C, contracting over B using the quantale.

    Parameters
    ----------
    other : Morphism
        Right morphism whose domain must match self's codomain.

    Returns
    -------
    ComposedMorphism
        The composed morphism.
    """
    if not isinstance(other, Morphism):
        return NotImplemented
    if self.codomain != other.domain:
        raise TypeError(
            f"cannot compose: codomain {self.codomain!r} != domain {other.domain!r}"
        )
    if not self._quantale.is_compatible(other._quantale):
        raise TypeError(
            f"incompatible quantales: {self._quantale!r} and {other._quantale!r}"
        )
    return ComposedMorphism(self, other)

__matmul__

__matmul__(other: Morphism) -> ProductMorphism

Tensor (parallel) product: self @ other.

Given self: A -> B and other: C -> D, produces a morphism A×C -> B×D whose tensor is the outer product via the quantale's tensor_op.

PARAMETER DESCRIPTION
other

Right morphism.

TYPE: Morphism

RETURNS DESCRIPTION
ProductMorphism

The product morphism.

Source code in src/quivers/core/morphisms.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def __matmul__(self, other: Morphism) -> ProductMorphism:
    """Tensor (parallel) product: self @ other.

    Given self: A -> B and other: C -> D, produces
    a morphism A×C -> B×D whose tensor is the outer product
    via the quantale's tensor_op.

    Parameters
    ----------
    other : Morphism
        Right morphism.

    Returns
    -------
    ProductMorphism
        The product morphism.
    """
    if not isinstance(other, Morphism):
        return NotImplemented
    return ProductMorphism(self, other)

marginalize

marginalize(*sets: SetObject) -> MarginalizedMorphism

Marginalize (join-reduce) over codomain components.

The codomain must be a ProductSet containing the sets to marginalize over. The result has a codomain with those components removed. Uses the quantale's join operation.

PARAMETER DESCRIPTION
*sets

Codomain components to marginalize over.

TYPE: SetObject DEFAULT: ()

RETURNS DESCRIPTION
MarginalizedMorphism

The marginalized morphism.

Source code in src/quivers/core/morphisms.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
def marginalize(self, *sets: SetObject) -> MarginalizedMorphism:
    """Marginalize (join-reduce) over codomain components.

    The codomain must be a ProductSet containing the sets to
    marginalize over. The result has a codomain with those
    components removed. Uses the quantale's join operation.

    Parameters
    ----------
    *sets : SetObject
        Codomain components to marginalize over.

    Returns
    -------
    MarginalizedMorphism
        The marginalized morphism.
    """
    return MarginalizedMorphism(self, sets)

ObservedMorphism

ObservedMorphism(domain: SetObject, codomain: SetObject, data: Tensor, quantale: Quantale | None = None)

Bases: Morphism

A morphism with a fixed (non-learnable) tensor.

PARAMETER DESCRIPTION
domain

Source object.

TYPE: SetObject

codomain

Target object.

TYPE: SetObject

data

Fixed tensor of shape (domain.shape, codomain.shape).

TYPE: Tensor

quantale

The enrichment algebra. Defaults to PRODUCT_FUZZY.

TYPE: Quantale or None DEFAULT: None

Source code in src/quivers/core/morphisms.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
def __init__(
    self,
    domain: SetObject,
    codomain: SetObject,
    data: torch.Tensor,
    quantale: Quantale | None = None,
) -> None:
    super().__init__(domain, codomain, quantale=quantale)
    expected = self.tensor_shape
    if data.shape != expected:
        raise ValueError(
            f"data shape {data.shape} does not match expected {expected}"
        )
    self._module = _MorphismModule()
    self._module.register_buffer("data", data)

LatentMorphism

LatentMorphism(domain: SetObject, codomain: SetObject, init_scale: float = 0.5, quantale: Quantale | None = None)

Bases: Morphism

A learnable morphism backed by an nn.Parameter.

Stores unconstrained real-valued parameters and applies sigmoid to produce [0,1]-valued fuzzy relation entries.

PARAMETER DESCRIPTION
domain

Source object.

TYPE: SetObject

codomain

Target object.

TYPE: SetObject

init_scale

Standard deviation of the normal initialization for the unconstrained parameters. Default 0.5 (sigmoid maps this to roughly uniform over [0.3, 0.7]).

TYPE: float DEFAULT: 0.5

quantale

The enrichment algebra. Defaults to PRODUCT_FUZZY.

TYPE: Quantale or None DEFAULT: None

Source code in src/quivers/core/morphisms.py
224
225
226
227
228
229
230
231
232
233
234
235
def __init__(
    self,
    domain: SetObject,
    codomain: SetObject,
    init_scale: float = 0.5,
    quantale: Quantale | None = None,
) -> None:
    super().__init__(domain, codomain, quantale=quantale)
    shape = self.tensor_shape
    self._module = _MorphismModule()
    raw = nn.Parameter(torch.randn(shape) * init_scale)
    self._module.register_parameter("raw", raw)

raw property

raw: Parameter

Unconstrained parameter tensor.

tensor property

tensor: Tensor

Sigmoid-constrained tensor with values in (0, 1).

ComposedMorphism

ComposedMorphism(left: Morphism, right: Morphism)

Bases: Morphism

V-enriched composition of two morphisms.

Given left: A -> B and right: B -> C, produces A -> C by contracting over B using the quantale's compose method.

PARAMETER DESCRIPTION
left

Left morphism (applied first).

TYPE: Morphism

right

Right morphism (applied second).

TYPE: Morphism

Source code in src/quivers/core/morphisms.py
274
275
276
277
278
279
def __init__(self, left: Morphism, right: Morphism) -> None:
    n_contract = left.codomain.ndim
    super().__init__(left.domain, right.codomain, quantale=left._quantale)
    self._left = left
    self._right = right
    self._n_contract = n_contract

left property

left: Morphism

Left (first) morphism.

right property

right: Morphism

Right (second) morphism.

ProductMorphism

ProductMorphism(left: Morphism, right: Morphism)

Bases: Morphism

Tensor (parallel) product of two morphisms.

Given left: A -> B and right: C -> D, produces A×C -> B×D. The tensor is the outer product of the two component tensors via the quantale's tensor_op.

PARAMETER DESCRIPTION
left

Left morphism.

TYPE: Morphism

right

Right morphism.

TYPE: Morphism

Source code in src/quivers/core/morphisms.py
325
326
327
328
329
330
def __init__(self, left: Morphism, right: Morphism) -> None:
    domain = ProductSet(components=(left.domain, right.domain))
    codomain = ProductSet(components=(left.codomain, right.codomain))
    super().__init__(domain, codomain, quantale=left._quantale)
    self._left = left
    self._right = right

MarginalizedMorphism

MarginalizedMorphism(inner: Morphism, sets_to_marginalize: tuple[SetObject, ...] | list[SetObject])

Bases: Morphism

Morphism with codomain dimensions marginalized via the quantale's join.

Given an inner morphism A -> B × C and a set B to marginalize, produces A -> C by join-reduction over B's dimensions.

PARAMETER DESCRIPTION
inner

The morphism to marginalize.

TYPE: Morphism

sets_to_marginalize

Codomain components to marginalize over.

TYPE: tuple of SetObject

Source code in src/quivers/core/morphisms.py
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
def __init__(
    self,
    inner: Morphism,
    sets_to_marginalize: tuple[SetObject, ...] | list[SetObject],
) -> None:
    codomain = inner.codomain
    sets_to_marginalize = tuple(sets_to_marginalize)
    if not isinstance(codomain, ProductSet):
        raise TypeError(
            f"can only marginalize over ProductSet codomain, got {type(codomain).__name__}"
        )
    n_domain = inner.domain.ndim
    remaining_components: list[SetObject] = []
    dims_to_reduce: list[int] = []
    offset = n_domain
    for component in codomain.components:
        if component in sets_to_marginalize:
            for d in range(component.ndim):
                dims_to_reduce.append(offset + d)
        else:
            remaining_components.append(component)
        offset += component.ndim
    if not dims_to_reduce:
        raise ValueError("none of the specified sets found in codomain components")
    if len(remaining_components) == 0:
        raise ValueError("cannot marginalize all codomain components")
    elif len(remaining_components) == 1:
        new_codomain = remaining_components[0]
    else:
        new_codomain = ProductSet(components=tuple(remaining_components))
    super().__init__(inner.domain, new_codomain, quantale=inner._quantale)
    self._inner = inner
    self._dims_to_reduce = tuple(dims_to_reduce)

FunctorMorphism

FunctorMorphism(functor: Functor, inner: Morphism, domain: SetObject, codomain: SetObject)

Bases: Morphism

Lazy image of a morphism under a functor.

Recomputes the tensor on each access from the inner morphism, preserving gradient flow through the functor's map_tensor method. No additional parameters beyond those of the inner morphism.

PARAMETER DESCRIPTION
functor

The functor that produced this morphism.

TYPE: Functor

inner

The original morphism being mapped.

TYPE: Morphism

domain

The image of the inner morphism's domain under the functor.

TYPE: SetObject

codomain

The image of the inner morphism's codomain under the functor.

TYPE: SetObject

Source code in src/quivers/core/morphisms.py
438
439
440
441
442
443
def __init__(
    self, functor: Functor, inner: Morphism, domain: SetObject, codomain: SetObject
) -> None:
    super().__init__(domain, codomain, quantale=inner._quantale)
    self._functor = functor
    self._inner = inner

inner property

inner: Morphism

The original morphism being mapped.

RepeatMorphism

RepeatMorphism(inner: Morphism, n: int = 1)

Bases: Morphism

Runtime-variable iterated composition (matrix power).

Wraps an endomorphism f : X -> X and computes f^n at runtime, where n can be changed between calls. Uses repeated squaring for O(log n) quantale compositions.

For an endomorphism T : S -> S under a quantale, T^n is the n-fold Kleisli composition. Under the product_fuzzy quantale with stochastic matrices, this is standard matrix power.

PARAMETER DESCRIPTION
inner

An endomorphism (domain must equal codomain).

TYPE: Morphism

n

Initial number of repetitions (default 1). Can be changed later via the n_steps property.

TYPE: int DEFAULT: 1

RAISES DESCRIPTION
TypeError

If the inner morphism is not an endomorphism.

ValueError

If n < 1.

Examples:

>>> T = morphism(S, S)
>>> rep = RepeatMorphism(T, n=5)
>>> rep.tensor  # computes T^5
>>> rep.n_steps = 10
>>> rep.tensor  # now computes T^10
Source code in src/quivers/core/morphisms.py
501
502
503
504
505
506
507
508
509
510
511
def __init__(self, inner: Morphism, n: int = 1) -> None:
    if inner.domain != inner.codomain:
        raise TypeError(
            f"repeat requires an endomorphism, got {inner.domain!r} -> {inner.codomain!r}"
        )
    if n < 1:
        raise ValueError(f"n must be >= 1, got {n}")
    super().__init__(inner.domain, inner.codomain, quantale=inner._quantale)
    self._inner = inner
    self._n = n
    self._n_contract = inner.codomain.ndim

inner property

inner: Morphism

The base endomorphism.

n_steps property writable

n_steps: int

Number of iterated compositions.

tensor property

tensor: Tensor

Compute the n-fold composition via repeated squaring.

RETURNS DESCRIPTION
Tensor

The tensor for f^n, same shape as the inner morphism.

morphism

morphism(domain: SetObject, codomain: SetObject, init_scale: float = 0.5, quantale: Quantale | None = None) -> LatentMorphism

Create a latent (learnable) morphism.

PARAMETER DESCRIPTION
domain

Source object.

TYPE: SetObject

codomain

Target object.

TYPE: SetObject

init_scale

Initialization scale for unconstrained parameters.

TYPE: float DEFAULT: 0.5

quantale

The enrichment algebra. Defaults to PRODUCT_FUZZY.

TYPE: Quantale or None DEFAULT: None

RETURNS DESCRIPTION
LatentMorphism

A learnable morphism.

Source code in src/quivers/core/morphisms.py
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
def morphism(
    domain: SetObject,
    codomain: SetObject,
    init_scale: float = 0.5,
    quantale: Quantale | None = None,
) -> LatentMorphism:
    """Create a latent (learnable) morphism.

    Parameters
    ----------
    domain : SetObject
        Source object.
    codomain : SetObject
        Target object.
    init_scale : float
        Initialization scale for unconstrained parameters.
    quantale : Quantale or None
        The enrichment algebra. Defaults to PRODUCT_FUZZY.

    Returns
    -------
    LatentMorphism
        A learnable morphism.
    """
    return LatentMorphism(domain, codomain, init_scale=init_scale, quantale=quantale)

observed

observed(domain: SetObject, codomain: SetObject, data: Tensor, quantale: Quantale | None = None) -> ObservedMorphism

Create an observed (fixed) morphism.

PARAMETER DESCRIPTION
domain

Source object.

TYPE: SetObject

codomain

Target object.

TYPE: SetObject

data

Fixed tensor.

TYPE: Tensor

quantale

The enrichment algebra. Defaults to PRODUCT_FUZZY.

TYPE: Quantale or None DEFAULT: None

RETURNS DESCRIPTION
ObservedMorphism

A fixed morphism.

Source code in src/quivers/core/morphisms.py
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
def observed(
    domain: SetObject,
    codomain: SetObject,
    data: torch.Tensor,
    quantale: Quantale | None = None,
) -> ObservedMorphism:
    """Create an observed (fixed) morphism.

    Parameters
    ----------
    domain : SetObject
        Source object.
    codomain : SetObject
        Target object.
    data : torch.Tensor
        Fixed tensor.
    quantale : Quantale or None
        The enrichment algebra. Defaults to PRODUCT_FUZZY.

    Returns
    -------
    ObservedMorphism
        A fixed morphism.
    """
    return ObservedMorphism(domain, codomain, data, quantale=quantale)

identity

identity(obj: SetObject, quantale: Quantale | None = None) -> ObservedMorphism

Create the identity morphism on an object.

Returns an observed morphism obj -> obj whose tensor is the identity: unit on the diagonal, zero elsewhere.

PARAMETER DESCRIPTION
obj

The object to create an identity for.

TYPE: SetObject

quantale

The enrichment algebra. Defaults to PRODUCT_FUZZY.

TYPE: Quantale or None DEFAULT: None

RETURNS DESCRIPTION
ObservedMorphism

The identity morphism.

Source code in src/quivers/core/morphisms.py
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
def identity(obj: SetObject, quantale: Quantale | None = None) -> ObservedMorphism:
    """Create the identity morphism on an object.

    Returns an observed morphism obj -> obj whose tensor is the
    identity: unit on the diagonal, zero elsewhere.

    Parameters
    ----------
    obj : SetObject
        The object to create an identity for.
    quantale : Quantale or None
        The enrichment algebra. Defaults to PRODUCT_FUZZY.

    Returns
    -------
    ObservedMorphism
        The identity morphism.
    """
    q = quantale if quantale is not None else PRODUCT_FUZZY
    data = q.identity_tensor(obj.shape)
    return ObservedMorphism(obj, obj, data, quantale=q)