Extra Quantales

Specialized quantale implementations for domain-specific applications and extended theory.

extra_quantales

Additional quantales for V-enriched categories.

This module extends the base quantales (ProductFuzzy, BooleanQuantale) with three additional enrichment algebras:

LukasiewiczQuantale — [0,1] with Łukasiewicz t-norm
GodelQuantale       — [0,1] with Gödel (min) t-norm
TropicalQuantale    — [0,∞] with + as tensor, inf as join

Each quantale gives a different category of relations:

- Łukasiewicz: Resource-sensitive fuzzy relations.
  ⊗ = max(a + b - 1, 0), good for reasoning about bounded resources.

- Gödel: Possibilistic relations with min semantics.
  ⊗ = min(a, b), giving the weakest fuzzy logic.

- Tropical: Lawvere metric spaces (generalized metrics).
  ⊗ = a + b (distances add), ⋁ = inf (shortest path).
  Note: values are in [0, ∞], unit = 0, zero = ∞.

LukasiewiczQuantale

Bases: Quantale

[0,1] with Łukasiewicz t-norm and bounded sum.

The Łukasiewicz t-norm is the strongest continuous t-norm:

⊗ = Łukasiewicz:   a ⊗ b = max(a + b - 1, 0)
⋁ = bounded sum:   ⋁_i x_i = min(1, ∑_i x_i)
⋀ = min:           ⋀_i x_i = min_i x_i
¬ = strong neg:    ¬a = 1 - a
I = 1.0
⊥ = 0.0

This quantale is useful for resource-sensitive reasoning where combining evidence can "cancel out" (unlike product t-norm).

tensor_op

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

Łukasiewicz t-norm: max(a + b - 1, 0).

Source code in src/quivers/core/extra_quantales.py
52
53
54
def tensor_op(self, a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:
    """Łukasiewicz t-norm: max(a + b - 1, 0)."""
    return (a + b - 1.0).clamp(min=0.0)

join

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

Bounded sum: min(1, ∑_i x_i).

Source code in src/quivers/core/extra_quantales.py
56
57
58
59
60
61
62
def join(self, t: torch.Tensor, dim: int | tuple[int, ...]) -> torch.Tensor:
    """Bounded sum: min(1, ∑_i x_i)."""
    if isinstance(dim, int):
        dim = (dim,)

    result = t.sum(dim=dim).clamp(max=1.0)
    return result

meet

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

Min: ⋀_i x_i = min_i x_i.

Source code in src/quivers/core/extra_quantales.py
64
65
66
67
68
69
70
71
72
73
74
def meet(self, t: torch.Tensor, dim: int | tuple[int, ...]) -> torch.Tensor:
    """Min: ⋀_i x_i = min_i x_i."""
    if isinstance(dim, int):
        dim = (dim,)

    result = t

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

    return result

negate

negate(t: Tensor) -> Tensor

Strong negation: ¬a = 1 - a.

Source code in src/quivers/core/extra_quantales.py
76
77
78
def negate(self, t: torch.Tensor) -> torch.Tensor:
    """Strong negation: ¬a = 1 - a."""
    return 1.0 - t

GodelQuantale

Bases: Quantale

[0,1] with Gödel (min) t-norm.

The weakest continuous t-norm:

⊗ = min:       a ⊗ b = min(a, b)
⋁ = max:       ⋁_i x_i = max_i x_i
⋀ = min:       ⋀_i x_i = min_i x_i
¬ = Gödel neg: ¬a = 1 if a = 0, else 0
I = 1.0
⊥ = 0.0

In a Gödel-enriched category, composition computes the "best worst-case" path — the minimax composition familiar from fuzzy graph theory.

tensor_op

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

Gödel t-norm: min(a, b).

Source code in src/quivers/core/extra_quantales.py
110
111
112
def tensor_op(self, a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:
    """Gödel t-norm: min(a, b)."""
    return torch.min(a, b)

join

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

Max: ⋁_i x_i = max_i x_i.

Source code in src/quivers/core/extra_quantales.py
114
115
116
117
118
119
120
121
122
123
124
def join(self, t: torch.Tensor, dim: int | tuple[int, ...]) -> torch.Tensor:
    """Max: ⋁_i x_i = max_i x_i."""
    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

Min: ⋀_i x_i = min_i x_i.

Source code in src/quivers/core/extra_quantales.py
126
127
128
129
130
131
132
133
134
135
136
def meet(self, t: torch.Tensor, dim: int | tuple[int, ...]) -> torch.Tensor:
    """Min: ⋀_i x_i = min_i x_i."""
    if isinstance(dim, int):
        dim = (dim,)

    result = t

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

    return result

negate

negate(t: Tensor) -> Tensor

Gödel negation: ¬a = 1 if a == 0, else 0.

Source code in src/quivers/core/extra_quantales.py
138
139
140
def negate(self, t: torch.Tensor) -> torch.Tensor:
    """Gödel negation: ¬a = 1 if a == 0, else 0."""
    return (t == 0.0).float()

TropicalQuantale

Bases: Quantale

[0, ∞] with addition and infimum (tropical semiring).

This is the Lawvere enrichment for generalized metric spaces:

⊗ = addition:     a ⊗ b = a + b (distances compose additively)
⋁ = infimum:      ⋁_i x_i = min_i x_i (shortest path)
⋀ = supremum:     ⋀_i x_i = max_i x_i (longest path)
¬ = n/a:          negation is not well-defined for metrics
I = 0.0           (zero distance)
⊥ = ∞             (infinite distance / unreachable)

Composition computes shortest-path distances:

(g ∘ f)(a, c) = inf_b [f(a, b) + g(b, c)]

This is the tropical matrix multiplication, a.k.a. the (min, +) semiring product.

Note

We use torch.inf for ⊥ (unreachable) and 0.0 for I (identity). The identity tensor has 0 on the diagonal and ∞ elsewhere.

tensor_op

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

Tropical tensor: a + b.

Source code in src/quivers/core/extra_quantales.py
180
181
182
def tensor_op(self, a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:
    """Tropical tensor: a + b."""
    return a + b

join

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

Infimum (min): shortest path.

Source code in src/quivers/core/extra_quantales.py
184
185
186
187
188
189
190
191
192
193
194
def join(self, t: torch.Tensor, dim: int | tuple[int, ...]) -> torch.Tensor:
    """Infimum (min): shortest path."""
    if isinstance(dim, int):
        dim = (dim,)

    result = t

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

    return result

meet

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

Supremum (max): longest path.

Source code in src/quivers/core/extra_quantales.py
196
197
198
199
200
201
202
203
204
205
206
def meet(self, t: torch.Tensor, dim: int | tuple[int, ...]) -> torch.Tensor:
    """Supremum (max): longest path."""
    if isinstance(dim, int):
        dim = (dim,)

    result = t

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

    return result

negate

negate(t: Tensor) -> Tensor

Negation is not meaningful for the tropical quantale.

Returns the additive inverse as a best-effort approximation, but note this is outside [0, ∞] for positive values.

RAISES DESCRIPTION
NotImplementedError

Always, since tropical negation is not well-defined.

Source code in src/quivers/core/extra_quantales.py
208
209
210
211
212
213
214
215
216
217
218
219
220
221
def negate(self, t: torch.Tensor) -> torch.Tensor:
    """Negation is not meaningful for the tropical quantale.

    Returns the additive inverse as a best-effort approximation,
    but note this is outside [0, ∞] for positive values.

    Raises
    ------
    NotImplementedError
        Always, since tropical negation is not well-defined.
    """
    raise NotImplementedError(
        "negation is not well-defined for the tropical quantale"
    )

identity_tensor

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

Identity with 0 on diagonal and ∞ elsewhere.

Override because the default uses self.zero for off-diagonal and self.unit for diagonal, which is correct here (0 on diag, ∞ off), but we use torch.inf explicitly for clarity.

PARAMETER DESCRIPTION
obj_shape

Shape of the object.

TYPE: tuple[int, ...]

RETURNS DESCRIPTION
Tensor

Identity tensor.

Source code in src/quivers/core/extra_quantales.py
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
def identity_tensor(self, obj_shape: tuple[int, ...]) -> torch.Tensor:
    """Identity with 0 on diagonal and ∞ elsewhere.

    Override because the default uses self.zero for off-diagonal
    and self.unit for diagonal, which is correct here (0 on diag,
    ∞ off), but we use torch.inf explicitly for clarity.

    Parameters
    ----------
    obj_shape : tuple[int, ...]
        Shape of the object.

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

    if ndim == 1:
        n = obj_shape[0]

        for i in range(n):
            result[i, i] = 0.0

    else:
        for idx in itertools.product(*(range(s) for s in obj_shape)):
            result[idx + idx] = 0.0

    return result