Skip to content

math_spec.operators

The closed set of built-in operators and their call shapes.

One home for each signature: a composition is a macro, and math the language cannot say is a declared escape:.

BUILTINS = {'sum': Builtin('sum(<expr>), sum(<expr>, over=<dim|relation.columns>) or sum(<expr>, by=<relation.columns>)', dimension_kwargs=('over',), relation_kwargs=('by',), optional_kwargs=('over', 'by'), at_most_one_of=('over', 'by')), 'at': Builtin('at(<expr>, by=<relation>, over=<column>, into=<column>)', relation_kwargs=('by',), role_kwargs=('over', 'into'), with_relation=('over', 'into')), 'sum_back': Builtin("sum_back(<expr>, along=<dim|relation.key column>, window=<n|parameter>[, edge='wrap'][, within=<column>])", dimension_kwargs=('along',), role_kwargs=('within',), required_value_kwargs=('window',), edge_kwargs=('edge',), optional_kwargs=('within',)), 'shift': Builtin("shift(<expr>, along=<dim|relation.key column>, offset=<n>[, edge='wrap'|<number>][, within=<column>])", dimension_kwargs=('along',), role_kwargs=('within',), required_value_kwargs=('offset',), edge_kwargs=('edge',), optional_kwargs=('within',)), 'dual': Builtin('dual(<constraint>)')} module-attribute #

BUILTIN_NAMES = frozenset(BUILTINS) module-attribute #

EDGE_WRAP = 'wrap' module-attribute #

PARTITION_NAMES_ITS_GROUP = 'A partition names the value columns it groups by, so that a relation may gain a value column without changing what this call means.' module-attribute #

Builtin(usage, dimension_kwargs=(), relation_kwargs=(), role_kwargs=(), edge_kwargs=(), required_value_kwargs=(), optional_kwargs=(), at_most_one_of=(), with_relation=()) dataclass #

The call shape of one built-in operator.

Keyword arguments come in four kinds, and the kind decides what resolution turns the value into: dimension_kwargs name a dimension (sum(x, over=generator)) or a relation's key column (sum(x, over=zone_of.generator)); relation_kwargs name a relation and the columns the call lands on (sum(x, by=zone_of.zone)); edge_kwargs take a closed keyword or a number; required_value_kwargs are ordinary values that must be present — a number, never a name to resolve (shift(..., offset=1)).

Every operator takes one positional argument, the expression; every dimension or relation it names arrives in a kwarg value, which is what lets a macro pass one as a formal. usage is the wording every refusal quotes back.

at_most_one_of = () class-attribute instance-attribute #

dimension_kwargs = () class-attribute instance-attribute #

edge_kwargs = () class-attribute instance-attribute #

optional_kwargs = () class-attribute instance-attribute #

relation_kwargs = () class-attribute instance-attribute #

required property #

Every keyword the call must carry.

required_value_kwargs = () class-attribute instance-attribute #

role_kwargs = () class-attribute instance-attribute #

usage instance-attribute #

with_relation = () class-attribute instance-attribute #

kind_of(kwarg) #

What resolution turns the value of kwarg into, or None where the operator does not declare it.

A dimension — or the key column of a relation, which names one — a relation, a value column of one, an edge policy, or a plain value.

Source code in src/math_spec/operators.py
def kind_of(self, kwarg: str) -> Literal['dimension', 'relation', 'role', 'edge', 'value'] | None:
    """What resolution turns the value of *kwarg* into, or ``None`` where the operator does not declare it.

    A dimension — or the key column of a relation, which names one — a
    relation, a value column of one, an edge policy, or a plain value.
    """
    if kwarg in self.dimension_kwargs:
        return 'dimension'
    if kwarg in self.relation_kwargs:
        return 'relation'
    if kwarg in self.role_kwargs:
        return 'role'
    if kwarg in self.edge_kwargs:
        return 'edge'
    if kwarg in self.required_value_kwargs:
        return 'value'
    return None

both_ends_error(name) #

Why a grouping names one end of itself: over= and by= are the two ways to say the same grouping.

Source code in src/math_spec/operators.py
def both_ends_error(name: str) -> str:
    """Why a grouping names one end of itself: ``over=`` and ``by=`` are the two ways to say the same grouping."""
    return (
        f'{name}() names both ends of one grouping: over= says which columns it reads from and '
        f'by= says which columns it lands on, and the relation supplies the other end.\n'
        f'Write: {BUILTINS[name].usage}'
    )

call_shape_error(name, positional, kwargs) #

Why a call to name does not fit its signature; None if it fits.

Source code in src/math_spec/operators.py
def call_shape_error(name: str, positional: int, kwargs: Iterable[str]) -> str | None:
    """Why a call to *name* does not fit its signature; ``None`` if it fits."""
    builtin = BUILTINS[name]
    keys = set(kwargs)
    if len(keys & set(builtin.at_most_one_of)) > 1:
        return both_ends_error(name)
    optional = {*builtin.edge_kwargs, *builtin.optional_kwargs}
    reads = bool(keys & set(builtin.relation_kwargs))
    required = builtin.required | frozenset(builtin.with_relation) if reads else builtin.required
    optional |= set() if reads else set(builtin.with_relation)
    if reads and (unsaid := sorted(frozenset(builtin.with_relation) - keys)):
        return unsaid_ends_error(name, unsaid)
    fits = positional == 1 and keys - optional == required
    return None if fits else f'{name}() expects {builtin.usage}'

edge_error(name, given) #

Why an edge= value is not one the language has.

Source code in src/math_spec/operators.py
def edge_error(name: str, given: str) -> str:
    """Why an ``edge=`` value is not one the language has."""
    return (
        f'{name}(edge={given}) is not an edge policy.\n'
        f"Write edge='{EDGE_WRAP}' for a cyclic translation, a number for the "
        f'value the vacated positions contribute, or omit it and they are '
        f'absent — which drops the row.'
    )

unknown_operator_message(name) #

The one wording for "that is not an operator".

Source code in src/math_spec/operators.py
def unknown_operator_message(name: str) -> str:
    """The one wording for "that is not an operator"."""
    return (
        f"Unknown operator '{name}'.\n"
        f'Available: {sorted(BUILTIN_NAMES)}\n'
        f"Define '{name}' as a macro under 'macros:' if it composes built-ins; "
        f'if the math is not sayable in the language, use a declared escape.'
    )

unsaid_ends_error(name, unsaid) #

Why a call through a relation has to write every column it reads: both ends of a read, the group of a partition.

Source code in src/math_spec/operators.py
def unsaid_ends_error(name: str, unsaid: list[str]) -> str:
    """Why a call through a relation has to write every column it reads: both ends of a read, the group of a partition."""
    reason = (
        PARTITION_NAMES_ITS_GROUP
        if 'within' in BUILTINS[name].with_relation
        else 'A call names both of its ends, so that a relation may gain a value column without changing what this call means.'
    )
    return (
        f'{name}() through a relation leaves {", ".join(f"{k}=" for k in unsaid)} unsaid.\n'
        f'{reason}\n'
        f'Write: {BUILTINS[name].usage}'
    )