Categories

Category types for categorial grammars. Provides atomic, slash, product, unit, and modal categories plus a finite CategorySystem for chart parsers.

The category types form the free algebra generated by atoms and type constructors. CategorySystem.from_generators() enumerates this algebra up to a given depth, enabling fully parameterizable grammar construction for any formalism (CCG, Lambek, multimodal TLG, or bespoke).

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
223
224
225
226
227
228
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
230
231
232
233
234
@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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
@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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
@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(
                f"unknown constructor {name!r}; available: "
                f"{sorted(_BUILTIN_CONSTRUCTORS.keys())}"
            )
        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
297
298
299
300
301
302
303
304
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
306
307
308
309
310
311
312
313
314
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
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
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
344
345
346
347
348
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]