Quantales

Ordered algebraic structures for morphism composition and enriched category theory.

quantales

Quantales: enrichment algebras for V-enriched categories.

A commutative quantale Q = (L, ⊗, ⋁, ⋀, ¬, I, ⊥) provides the algebraic structure that parameterizes composition in a V-enriched category:

(g ∘ f)(a, c) = ⋁_b f(a, b) ⊗ g(b, c)

Different quantales yield different categories of relations:

- BooleanQuantale:  {0,1} with ∧, ∨         → Rel (crisp relations)
- ProductFuzzy:     [0,1] with ×, noisy-OR   → FuzzyRel (product t-norm)

The enrichment determines composition, identity, marginalization, and quantification, all derived from the quantale's operations.

Quantale

Bases: ABC

Abstract commutative quantale for V-enriched categories.

Subclasses must implement the six primitive operations. Composition and identity are derived but overridable.

name abstractmethod property

name: str

Human-readable name for this quantale.

unit abstractmethod property

unit: float

Unit element I of the monoidal product ⊗.

zero abstractmethod property

zero: float

Bottom element ⊥ (identity for ⋁).

tensor_op abstractmethod

tensor_op(a: Tensor, b: Tensor) -> Tensor

Monoidal product ⊗ (elementwise).

PARAMETER DESCRIPTION
a

Left operand.

TYPE: Tensor

b

Right operand (broadcastable with a).

TYPE: Tensor

RETURNS DESCRIPTION
Tensor

a ⊗ b, elementwise.

Source code in src/quivers/core/quantales.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@abstractmethod
def tensor_op(self, a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:
    """Monoidal product ⊗ (elementwise).

    Parameters
    ----------
    a : torch.Tensor
        Left operand.
    b : torch.Tensor
        Right operand (broadcastable with a).

    Returns
    -------
    torch.Tensor
        a ⊗ b, elementwise.
    """
    ...

join abstractmethod

join(t: Tensor, dim: int | tuple[int, ...]) -> Tensor

Join ⋁ — reduction for composition and existential (∃).

PARAMETER DESCRIPTION
t

Input tensor with values in L.

TYPE: Tensor

dim

Dimension(s) to reduce.

TYPE: int or tuple[int, ...]

RETURNS DESCRIPTION
Tensor

Reduced tensor.

Source code in src/quivers/core/quantales.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
@abstractmethod
def join(self, t: torch.Tensor, dim: int | tuple[int, ...]) -> torch.Tensor:
    """Join ⋁ — reduction for composition and existential (∃).

    Parameters
    ----------
    t : torch.Tensor
        Input tensor with values in L.
    dim : int or tuple[int, ...]
        Dimension(s) to reduce.

    Returns
    -------
    torch.Tensor
        Reduced tensor.
    """
    ...

meet abstractmethod

meet(t: Tensor, dim: int | tuple[int, ...]) -> Tensor

Meet ⋀ — reduction for universal quantification (∀).

PARAMETER DESCRIPTION
t

Input tensor with values in L.

TYPE: Tensor

dim

Dimension(s) to reduce.

TYPE: int or tuple[int, ...]

RETURNS DESCRIPTION
Tensor

Reduced tensor.

Source code in src/quivers/core/quantales.py
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
@abstractmethod
def meet(self, t: torch.Tensor, dim: int | tuple[int, ...]) -> torch.Tensor:
    """Meet ⋀ — reduction for universal quantification (∀).

    Parameters
    ----------
    t : torch.Tensor
        Input tensor with values in L.
    dim : int or tuple[int, ...]
        Dimension(s) to reduce.

    Returns
    -------
    torch.Tensor
        Reduced tensor.
    """
    ...

negate abstractmethod

negate(t: Tensor) -> Tensor

Complement / negation ¬.

PARAMETER DESCRIPTION
t

Input tensor.

TYPE: Tensor

RETURNS DESCRIPTION
Tensor

¬t, elementwise.

Source code in src/quivers/core/quantales.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
@abstractmethod
def negate(self, t: torch.Tensor) -> torch.Tensor:
    """Complement / negation ¬.

    Parameters
    ----------
    t : torch.Tensor
        Input tensor.

    Returns
    -------
    torch.Tensor
        ¬t, elementwise.
    """
    ...

compose

compose(m: Tensor, n: Tensor, n_contract: int) -> Tensor

V-enriched composition.

Computes: result[d..., c...] = ⋁_{s...} m[d..., s...] ⊗ n[s..., c...]

Override for numerical stability in specific quantales.

PARAMETER DESCRIPTION
m

Left tensor of shape (domain, shared).

TYPE: Tensor

n

Right tensor of shape (shared, codomain).

TYPE: Tensor

n_contract

Number of shared dimensions to contract.

TYPE: int

RETURNS DESCRIPTION
Tensor

Composed tensor of shape (domain, codomain).

Source code in src/quivers/core/quantales.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def compose(
    self,
    m: torch.Tensor,
    n: torch.Tensor,
    n_contract: int,
) -> torch.Tensor:
    """V-enriched composition.

    Computes: result[d..., c...] = ⋁_{s...} m[d..., s...] ⊗ n[s..., c...]

    Override for numerical stability in specific quantales.

    Parameters
    ----------
    m : torch.Tensor
        Left tensor of shape (*domain, *shared).
    n : torch.Tensor
        Right tensor of shape (*shared, *codomain).
    n_contract : int
        Number of shared dimensions to contract.

    Returns
    -------
    torch.Tensor
        Composed tensor of shape (*domain, *codomain).
    """
    if n_contract < 1:
        raise ValueError(f"n_contract must be >= 1, got {n_contract}")

    # validate shared dimensions
    shared_m = m.shape[-n_contract:]
    shared_n = n.shape[:n_contract]

    if shared_m != shared_n:
        raise ValueError(
            f"shared dimensions do not match: "
            f"m trailing {shared_m} != n leading {shared_n}"
        )

    n_domain = m.ndim - n_contract
    n_codomain = n.ndim - n_contract

    # broadcast for element-wise tensor_op
    m_expanded = m.reshape(*m.shape, *([1] * n_codomain))
    n_expanded = n.reshape(*([1] * n_domain), *n.shape)

    product = self.tensor_op(m_expanded, n_expanded)

    # join over shared dims
    contract_dims = tuple(range(n_domain, n_domain + n_contract))
    return self.join(product, dim=contract_dims)

identity_tensor

identity_tensor(obj_shape: tuple[int, ...]) -> Tensor

Identity morphism tensor for an object with given shape.

Returns a tensor of shape (obj_shape, obj_shape) with the unit value on the diagonal and zero elsewhere.

PARAMETER DESCRIPTION
obj_shape

Shape of the object (e.g., (n,) for FinSet(n)).

TYPE: tuple[int, ...]

RETURNS DESCRIPTION
Tensor

Identity tensor.

Source code in src/quivers/core/quantales.py
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
def identity_tensor(self, obj_shape: tuple[int, ...]) -> torch.Tensor:
    """Identity morphism tensor for an object with given shape.

    Returns a tensor of shape (*obj_shape, *obj_shape) with
    the unit value on the diagonal and zero elsewhere.

    Parameters
    ----------
    obj_shape : tuple[int, ...]
        Shape of the object (e.g., (n,) for FinSet(n)).

    Returns
    -------
    torch.Tensor
        Identity tensor.
    """
    full_shape = obj_shape + obj_shape
    result = torch.full(full_shape, self.zero)
    ndim = len(obj_shape)

    if ndim == 1:
        # simple case: (n, n) matrix
        n = obj_shape[0]

        for i in range(n):
            result[i, i] = self.unit

    else:
        # multi-dimensional: iterate over all index tuples
        for idx in itertools.product(*(range(s) for s in obj_shape)):
            result[idx + idx] = self.unit

    return result

is_compatible

is_compatible(other: Quantale) -> bool

Check if two quantales are compatible for composition.

PARAMETER DESCRIPTION
other

The other quantale.

TYPE: Quantale

RETURNS DESCRIPTION
bool

True if morphisms from these quantales can compose.

Source code in src/quivers/core/quantales.py
212
213
214
215
216
217
218
219
220
221
222
223
224
225
def is_compatible(self, other: Quantale) -> bool:
    """Check if two quantales are compatible for composition.

    Parameters
    ----------
    other : Quantale
        The other quantale.

    Returns
    -------
    bool
        True if morphisms from these quantales can compose.
    """
    return type(self) is type(other)

ProductFuzzy

Bases: Quantale

[0,1] with product t-norm and probabilistic sum (noisy-OR).

This is the enrichment for the Kleisli category of the fuzzy powerset monad with the product t-norm:

⊗ = product:      a ⊗ b = a * b
⋁ = noisy-OR:     ⋁_i x_i = 1 - ∏_i (1 - x_i)
⋀ = product:      ⋀_i x_i = ∏_i x_i
¬ = complement:    ¬a = 1 - a
I = 1.0
⊥ = 0.0

Composition uses log-space for numerical stability.

join

join(t: Tensor, dim: int | tuple[int, ...]) -> Tensor

Noisy-OR in log-space: 1 - exp(∑ log(1 - t)).

Source code in src/quivers/core/quantales.py
254
255
256
257
258
259
260
261
262
263
def join(self, t: torch.Tensor, dim: int | tuple[int, ...]) -> torch.Tensor:
    """Noisy-OR in log-space: 1 - exp(∑ log(1 - t))."""
    if isinstance(dim, int):
        dim = (dim,)

    t_clamped = clamp_probs(t)
    log_complement = torch.log1p(-t_clamped)
    sum_log = log_complement.sum(dim=dim)

    return -torch.expm1(sum_log)

meet

meet(t: Tensor, dim: int | tuple[int, ...]) -> Tensor

Product (fuzzy AND): ∏_i t_i.

Source code in src/quivers/core/quantales.py
265
266
267
268
269
270
271
272
273
274
275
def meet(self, t: torch.Tensor, dim: int | tuple[int, ...]) -> torch.Tensor:
    """Product (fuzzy AND): ∏_i t_i."""
    if isinstance(dim, int):
        dim = (dim,)

    result = t

    for d in sorted(dim, reverse=True):
        result = result.prod(dim=d)

    return result

compose

compose(m: Tensor, n: Tensor, n_contract: int) -> Tensor

Override for log-space numerical stability.

Computes noisy-OR contraction matching the existing noisy_or_contract implementation exactly.

Source code in src/quivers/core/quantales.py
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
def compose(
    self,
    m: torch.Tensor,
    n: torch.Tensor,
    n_contract: int,
) -> torch.Tensor:
    """Override for log-space numerical stability.

    Computes noisy-OR contraction matching the existing
    noisy_or_contract implementation exactly.
    """
    if n_contract < 1:
        raise ValueError(f"n_contract must be >= 1, got {n_contract}")

    shared_m = m.shape[-n_contract:]
    shared_n = n.shape[:n_contract]

    if shared_m != shared_n:
        raise ValueError(
            f"shared dimensions do not match: "
            f"m trailing {shared_m} != n leading {shared_n}"
        )

    n_domain = m.ndim - n_contract
    n_codomain = n.ndim - n_contract
    n_shared = n_contract

    m_expanded = m.reshape(*m.shape, *([1] * n_codomain))
    n_expanded = n.reshape(*([1] * n_domain), *n.shape)

    product = m_expanded * n_expanded

    # log-space noisy-OR for stability
    product_clamped = clamp_probs(product)
    log_complement = torch.log1p(-product_clamped)

    contract_dims = tuple(range(n_domain, n_domain + n_shared))
    sum_log = log_complement.sum(dim=contract_dims)

    return -torch.expm1(sum_log)

BooleanQuantale

Bases: Quantale

{0, 1} with logical AND and OR.

The enrichment for the category Rel of crisp binary relations:

⊗ = AND:     a ⊗ b = a ∧ b
⋁ = OR:      ⋁_i x_i = max_i x_i
⋀ = AND:     ⋀_i x_i = min_i x_i
¬ = NOT:     ¬a = 1 - a
I = 1.0
⊥ = 0.0

Works on float tensors with values in {0.0, 1.0}. Intermediate fuzzy values are rounded.

tensor_op

tensor_op(a: Tensor, b: Tensor) -> Tensor

Logical AND via product (exact for {0,1} inputs).

Source code in src/quivers/core/quantales.py
350
351
352
def tensor_op(self, a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:
    """Logical AND via product (exact for {0,1} inputs)."""
    return a * b

join

join(t: Tensor, dim: int | tuple[int, ...]) -> Tensor

Logical OR via iterated max.

Source code in src/quivers/core/quantales.py
354
355
356
357
358
359
360
361
362
363
364
def join(self, t: torch.Tensor, dim: int | tuple[int, ...]) -> torch.Tensor:
    """Logical OR via iterated max."""
    if isinstance(dim, int):
        dim = (dim,)

    result = t

    for d in sorted(dim, reverse=True):
        result = result.max(dim=d).values

    return result

meet

meet(t: Tensor, dim: int | tuple[int, ...]) -> Tensor

Logical AND via iterated min.

Source code in src/quivers/core/quantales.py
366
367
368
369
370
371
372
373
374
375
376
def meet(self, t: torch.Tensor, dim: int | tuple[int, ...]) -> torch.Tensor:
    """Logical AND via iterated min."""
    if isinstance(dim, int):
        dim = (dim,)

    result = t

    for d in sorted(dim, reverse=True):
        result = result.min(dim=d).values

    return result