Categories

Category types for categorial grammars: atomic, slash, product, unit, and modal categories, plus the finite CategorySystem used by chart parsers.

The category types form the free algebra generated by atoms and type constructors. CategorySystem.from_generators() enumerates this algebra to a specified depth, producing finite category inventories for CCG, Lambek, multimodal TLG, and custom rule systems.

categories

Category types for categorial grammars.

Provides the free algebra of grammatical categories generated by atoms and type constructors. This is the type system underlying chart parsers for CCG, the Lambek calculus, multimodal type-logical grammar, and any bespoke formalism assembled from biclosed monoidal primitives.

Atomic categories

AtomicCategory(name="S") — a basic category like S, NP, N, PP.

Slash categories (internal homs)

SlashCategory(result=R, argument=A, direction="/") — right residual: R/A SlashCategory(result=R, argument=A, direction="\") — left residual: R\A

Product categories (tensor product)

ProductCategory(left=L, right=R) — tensor: L ⊗ R

Unit category (monoidal unit)

UnitCategory() — the monoidal unit I satisfying I ⊗ A ≅ A ≅ A ⊗ I.

Category systems

A CategorySystem manages a finite inventory of categories with integer indices, supporting lookup and enumeration.

Category

Bases: TaggedUnion

Sum of grammatical category kinds.

Variants subclass this root and pin kind to a literal value; Category.model_validate dispatches on that discriminator.

AtomicCategory

Bases: Category

An atomic (primitive) grammatical category.

SlashCategory

Bases: Category

A complex category formed by a slash operator.

result/argument (forward): seeks argument on the right. result\argument (backward): seeks argument on the left.

These are the internal homs of a biclosed monoidal category: / is the right residual and \ is the left residual.

ProductCategory

Bases: Category

A product category (tensor product) for type-logical grammar.

Represents A ⊗ B in the Lambek calculus.

UnitCategory

Bases: Category

The monoidal unit category I.

Satisfies the unit laws: I ⊗ A ≅ A ≅ A ⊗ I.

ModalCategory

Bases: Category

A modal category: a unary type constructor applied to a category.

Represents modalities like ◇A (diamond) and □A (box) used in multimodal type-logical grammar (Moortgat 1997). Arbitrary named modalities are supported.

CategorySystem

CategorySystem(categories: list[Category] | None = None)

A finite inventory of grammatical categories with integer indices.

Manages the mapping between Category objects and their integer indices, used by chart parsers.

PARAMETER DESCRIPTION
categories

The initial category inventory.

TYPE: list of Category DEFAULT: None

Source code in src/quivers/stochastic/categories.py
264
265
266
267
268
269
def __init__(self, categories: list[Category] | None = None) -> None:
    self._cats: list[Category] = []
    self._index: dict[Category, int] = {}
    if categories:
        for cat in categories:
            self.add(cat)

size property

size: int

Number of categories in the system.

from_atoms classmethod

from_atoms(names: list[str]) -> CategorySystem

Create a category system from atomic category names.

Source code in src/quivers/stochastic/categories.py
271
272
273
274
275
@classmethod
def from_atoms(cls, names: list[str]) -> "CategorySystem":
    """Create a category system from atomic category names."""
    cats: list[Category] = [AtomicCategory(name=name) for name in names]
    return cls(cats)

from_atoms_and_slash_depth classmethod

from_atoms_and_slash_depth(names: list[str], max_depth: int = 1) -> CategorySystem

Create a category system with atomic categories and all slash combinations up to a given depth.

Source code in src/quivers/stochastic/categories.py
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
@classmethod
def from_atoms_and_slash_depth(
    cls,
    names: list[str],
    max_depth: int = 1,
) -> "CategorySystem":
    """Create a category system with atomic categories and all
    slash combinations up to a given depth.
    """
    system = cls.from_atoms(names)
    if max_depth < 1:
        return system
    for _ in range(max_depth):
        current = list(system._cats)
        for result in current:
            for arg in current:
                system.add(
                    SlashCategory(result=result, argument=arg, direction="/")
                )
                system.add(
                    SlashCategory(result=result, argument=arg, direction="\\")
                )
    return system

from_generators classmethod

from_generators(atoms: list[str], constructors: list[str] | None = None, max_depth: int = 1) -> CategorySystem

Create a category system from atoms and type constructors.

Built-in constructor names:

  • "slash" — generates all A/B and A\B
  • "product" — generates all A⊗B
  • "unit" — adds the monoidal unit I
  • "diamond" — generates ◇A for all A
  • "box" — generates □A for all A
Source code in src/quivers/stochastic/categories.py
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
328
329
330
331
332
333
@classmethod
def from_generators(
    cls,
    atoms: list[str],
    constructors: list[str] | None = None,
    max_depth: int = 1,
) -> "CategorySystem":
    """Create a category system from atoms and type constructors.

    Built-in constructor names:

    - ``"slash"`` — generates all A/B and A\\B
    - ``"product"`` — generates all A⊗B
    - ``"unit"`` — adds the monoidal unit I
    - ``"diamond"`` — generates ◇A for all A
    - ``"box"`` — generates □A for all A
    """
    if constructors is None:
        constructors = ["slash"]

    fns = []
    for name in constructors:
        if name not in _BUILTIN_CONSTRUCTORS:
            raise ValueError(constructor_name_error(name))
        fns.append(_BUILTIN_CONSTRUCTORS[name])

    system = cls.from_atoms(atoms)
    for _ in range(max_depth):
        current = list(system._cats)
        for fn in fns:
            for cat in fn(current):
                system.add(cat)
    return system

add

add(cat: Category) -> int

Add a category to the system; return its index.

Source code in src/quivers/stochastic/categories.py
335
336
337
338
339
340
341
342
def add(self, cat: Category) -> int:
    """Add a category to the system; return its index."""
    if cat in self._index:
        return self._index[cat]
    idx = len(self._cats)
    self._cats.append(cat)
    self._index[cat] = idx
    return idx

add_slash

add_slash(result: Category, argument: Category, direction: Literal['/', '\\']) -> int

Add a slash category and return its index.

Source code in src/quivers/stochastic/categories.py
344
345
346
347
348
349
350
351
352
def add_slash(
    self,
    result: Category,
    argument: Category,
    direction: Literal["/", "\\"],
) -> int:
    """Add a slash category and return its index."""
    cat = SlashCategory(result=result, argument=argument, direction=direction)
    return self.add(cat)

__getitem__

__getitem__(key: str | int | Category) -> Category | int

Look up by name (str), index (int), or category object.

Source code in src/quivers/stochastic/categories.py
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
def __getitem__(self, key: str | int | Category) -> Category | int:
    """Look up by name (str), index (int), or category object."""
    if isinstance(key, str):
        cat = AtomicCategory(name=key)
        if cat not in self._index:
            raise KeyError(f"category {key!r} not in system")
        return cat

    if isinstance(key, int):
        if key < 0 or key >= len(self._cats):
            raise KeyError(f"index {key} out of range")
        return self._cats[key]

    if isinstance(key, _CATEGORY_TYPES):
        if key not in self._index:
            raise KeyError(f"category {key!r} not in system")
        return self._index[key]

    raise TypeError(f"invalid key type: {type(key).__name__}")

index

index(cat: Category) -> int

Get the integer index of a category.

Source code in src/quivers/stochastic/categories.py
382
383
384
385
386
def index(self, cat: Category) -> int:
    """Get the integer index of a category."""
    if cat not in self._index:
        raise KeyError(f"category {cat!r} not in system")
    return self._index[cat]

constructor_name_error

constructor_name_error(name: str) -> str

Return the diagnostic for a residuated constructor name.

The message is shared by every validation site so a name is rejected in the same words whether it is caught when the universe is declared or when it is enumerated.

Source code in src/quivers/stochastic/categories.py
232
233
234
235
236
237
238
239
240
241
242
243
244
def constructor_name_error(name: str) -> str:
    """Return the diagnostic for a residuated constructor name.

    The message is shared by every validation site so a name is
    rejected in the same words whether it is caught when the
    universe is declared or when it is enumerated.
    """
    hint = _CONSTRUCTOR_HINTS.get(name)
    detail = f"; {hint}" if hint else ""
    return (
        f"unknown constructor {name!r}; available: "
        f"{sorted(BUILTIN_CONSTRUCTOR_NAMES)}{detail}"
    )