Skip to content

Adjunctions

The Adjunction interface, the FreeForgetfulAdjunction implementation, and their unit and counit operations.

adjunctions

Adjunctions between endofunctors.

An adjunction F ⊣ G consists of functors F (left adjoint) and G (right adjoint), together with natural transformations:

η: Id ⇒ G ∘ F    (unit)
ε: F ∘ G ⇒ Id    (counit)

satisfying the triangle identities:

ε_{F(A)} ∘ F(η_A) = id_{F(A)}
G(ε_A) ∘ η_{G(A)} = id_{G(A)}

Every adjunction induces a monad T = G ∘ F with η as unit and μ = G(ε_F) as multiplication.

This module provides:

Adjunction (abstract)
└── FreeForgetfulAdjunction — Free monoid ⊣ Forgetful

ForgetfulFunctor

Bases: Functor

The forgetful functor from FreeMonoid-algebras to FinSet.

On objects: A ↦ A (the underlying set). Since FreeMonoid is already a CoproductSet (hence a SetObject), the forgetful functor is the identity on the set level. On morphisms: identity on tensors.

In the adjunction Free ⊣ Forget, "Forget" maps A back to A as a bare set (no monoid structure). Since we represent everything as bare sets already, this is the identity functor.

map_object

map_object(obj: SetObject) -> SetObject

Identity on objects.

Source code in src/quivers/categorical/adjunctions.py
def map_object(self, obj: SetObject) -> SetObject:
    """Identity on objects."""
    return obj

map_morphism

map_morphism(morph: Morphism) -> FunctorMorphism

Identity on morphisms.

Source code in src/quivers/categorical/adjunctions.py
def map_morphism(self, morph: Morphism) -> FunctorMorphism:
    """Identity on morphisms."""
    return FunctorMorphism(self, morph, morph.domain, morph.codomain)

map_tensor

map_tensor(tensor: Tensor, algebra: object) -> Tensor

Identity on tensors.

Source code in src/quivers/categorical/adjunctions.py
def map_tensor(self, tensor: torch.Tensor, algebra: object) -> torch.Tensor:
    """Identity on tensors."""
    return tensor

Adjunction

Bases: ABC

Abstract adjunction F ⊣ G.

Subclasses must implement left, right, unit_component, and counit_component.

left abstractmethod property

left: Functor

The left adjoint F.

right abstractmethod property

right: Functor

The right adjoint G.

unit_component abstractmethod

unit_component(obj: SetObject) -> Morphism

The unit component η_A: A → G(F(A)).

PARAMETER DESCRIPTION
obj

The object A.

TYPE: SetObject

RETURNS DESCRIPTION
Morphism

The unit morphism η_A.

Source code in src/quivers/categorical/adjunctions.py
@abstractmethod
def unit_component(self, obj: SetObject) -> Morphism:
    """The unit component η_A: A → G(F(A)).

    Parameters
    ----------
    obj : SetObject
        The object A.

    Returns
    -------
    Morphism
        The unit morphism η_A.
    """
    ...

counit_component abstractmethod

counit_component(obj: SetObject) -> Morphism

The counit component ε_A: F(G(A)) → A.

PARAMETER DESCRIPTION
obj

The object A.

TYPE: SetObject

RETURNS DESCRIPTION
Morphism

The counit morphism ε_A.

Source code in src/quivers/categorical/adjunctions.py
@abstractmethod
def counit_component(self, obj: SetObject) -> Morphism:
    """The counit component ε_A: F(G(A)) → A.

    Parameters
    ----------
    obj : SetObject
        The object A.

    Returns
    -------
    Morphism
        The counit morphism ε_A.
    """
    ...

verify_triangle_left

verify_triangle_left(obj: SetObject, atol: float = 1e-05) -> bool

Verify left triangle identity: ε_{F(A)} ∘ F(η_A) ≈ id_{F(A)}.

PARAMETER DESCRIPTION
obj

The object A.

TYPE: SetObject

atol

Absolute tolerance.

TYPE: float DEFAULT: 1e-05

RETURNS DESCRIPTION
bool

True if the identity holds within tolerance.

Source code in src/quivers/categorical/adjunctions.py
def verify_triangle_left(self, obj: SetObject, atol: float = 1e-5) -> bool:
    """Verify left triangle identity: ε_{F(A)} ∘ F(η_A) ≈ id_{F(A)}.

    Parameters
    ----------
    obj : SetObject
        The object A.
    atol : float
        Absolute tolerance.

    Returns
    -------
    bool
        True if the identity holds within tolerance.
    """
    fa = self.left.map_object(obj)
    eta_a = self.unit_component(obj)
    f_eta = self.left.map_morphism(eta_a)
    eps_fa = self.counit_component(fa)
    result = (f_eta >> eps_fa).tensor
    expected = identity(fa).tensor

    return torch.allclose(result, expected, atol=atol)

verify_triangle_right

verify_triangle_right(obj: SetObject, atol: float = 1e-05) -> bool

Verify right triangle identity: G(ε_A) ∘ η_{G(A)} ≈ id_{G(A)}.

PARAMETER DESCRIPTION
obj

The object A.

TYPE: SetObject

atol

Absolute tolerance.

TYPE: float DEFAULT: 1e-05

RETURNS DESCRIPTION
bool

True if the identity holds within tolerance.

Source code in src/quivers/categorical/adjunctions.py
def verify_triangle_right(self, obj: SetObject, atol: float = 1e-5) -> bool:
    """Verify right triangle identity: G(ε_A) ∘ η_{G(A)} ≈ id_{G(A)}.

    Parameters
    ----------
    obj : SetObject
        The object A.
    atol : float
        Absolute tolerance.

    Returns
    -------
    bool
        True if the identity holds within tolerance.
    """
    ga = self.right.map_object(obj)
    eps_a = self.counit_component(obj)
    g_eps = self.right.map_morphism(eps_a)
    eta_ga = self.unit_component(ga)
    result = (eta_ga >> g_eps).tensor
    expected = identity(ga).tensor

    return torch.allclose(result, expected, atol=atol)

FreeForgetfulAdjunction

FreeForgetfulAdjunction(max_length: int)

Bases: Adjunction

The free-forgetful adjunction: Free ⊣ Forget.

Free: FinSet → FinSet via FreeMonoidFunctor (A ↦ A) Forget: identity at set level (A ↦ A* as bare set)

η_A: A → A embeds each element as a length-1 word. ε_A: A → A projects onto length-1 words (for A a FinSet).

PARAMETER DESCRIPTION
max_length

Maximum string length for the free monoid.

TYPE: int

Source code in src/quivers/categorical/adjunctions.py
def __init__(self, max_length: int) -> None:
    self._max_length = max_length
    self._free = FreeMonoidFunctor(max_length)
    self._forget = ForgetfulFunctor()

left property

left: Functor

The free functor.

right property

right: Functor

The forgetful functor.

unit_component

unit_component(obj: SetObject) -> Morphism

η_A: A → A* embeds as length-1 words.

PARAMETER DESCRIPTION
obj

Must be a FinSet.

TYPE: SetObject

RETURNS DESCRIPTION
Morphism

The embedding morphism.

Source code in src/quivers/categorical/adjunctions.py
def unit_component(self, obj: SetObject) -> Morphism:
    """η_A: A → A* embeds as length-1 words.

    Parameters
    ----------
    obj : SetObject
        Must be a FinSet.

    Returns
    -------
    Morphism
        The embedding morphism.
    """
    if not isinstance(obj, FinSet):
        obj = FinSet(name=getattr(obj, "name", repr(obj)), cardinality=obj.size)

    fm = FreeMonoid(generators=obj, max_length=self._max_length)
    n = obj.cardinality
    data = torch.zeros(n, fm.size)
    offset = fm.offset(1)

    for a in range(n):
        data[a, offset + a] = 1.0

    return observed(obj, fm, data)

counit_component

counit_component(obj: SetObject) -> Morphism

ε_A: Free(Forget(A)) → A.

For a FinSet A, this projects A* onto A by extracting the length-1 stratum. Length-1 words map to their element; all others (empty string, longer words) map to zero.

For a FreeMonoid A (needed by the left triangle identity), this is the monoid evaluation map (A) → A that concatenates formal words of words into single words.

PARAMETER DESCRIPTION
obj

The object A.

TYPE: SetObject

RETURNS DESCRIPTION
Morphism

The counit morphism.

Source code in src/quivers/categorical/adjunctions.py
def counit_component(self, obj: SetObject) -> Morphism:
    """ε_A: Free(Forget(A)) → A.

    For a FinSet A, this projects A* onto A by extracting the
    length-1 stratum. Length-1 words map to their element; all
    others (empty string, longer words) map to zero.

    For a FreeMonoid A* (needed by the left triangle identity),
    this is the monoid evaluation map (A*)* → A* that
    concatenates formal words of words into single words.

    Parameters
    ----------
    obj : SetObject
        The object A.

    Returns
    -------
    Morphism
        The counit morphism.
    """
    if isinstance(obj, FreeMonoid):
        return self._counit_at_free_monoid(obj)

    if not isinstance(obj, FinSet):
        obj = FinSet(name=getattr(obj, "name", repr(obj)), cardinality=obj.size)

    fm = FreeMonoid(generators=obj, max_length=self._max_length)
    data = torch.zeros(fm.size, obj.cardinality)

    # length-1 words: project to the corresponding element
    offset = fm.offset(1)

    for a in range(obj.cardinality):
        data[offset + a, a] = 1.0

    return observed(fm, obj, data)