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:.

AMOUNTS = {'shift': Amount('offset', 'A named offset carries its sign in its values, so that one row pointing backwards says so where the data is read — negate the column instead.', 'a permutation rather than a lag', -math.inf, 'must be a whole number, or the name of an integer parameter when the offset differs per entity — a lead time, a transit time, a minimum up time.'), 'sum_back': Amount('width', "A width counts positions and so has no direction; which way a window reaches is the operator's own name rather than the sign of its width.", 'a different window at every position, which is no longer "the last n"', 1, 'needs a whole number of positions of at least 1, or the name of an integer parameter when the window differs per entity. A width of 1 is the operand itself.')} module-attribute #

BUILTINS = {'sum': Builtin('sum(<expr>), sum(<expr>, over=<dim>) or sum(<expr>, by=<relation>, over=<column>, into=<column>)', relation_kwargs=('by',), role_kwargs=('into',), dimension_or_role_kwargs=('over',), optional_kwargs=('by',), with_relation=('over', 'into')), '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>, window=<n|parameter>[, edge='wrap'][, by=<relation>, within=<column>])", dimension_kwargs=('along',), relation_kwargs=('by',), role_kwargs=('within',), required_value_kwargs=('window',), edge_kwargs=('edge',), optional_kwargs=('by',), with_relation=('within',)), 'shift': Builtin("shift(<expr>, along=<dim>, offset=<n>[, edge='wrap'|<number>][, by=<relation>, within=<column>])", dimension_kwargs=('along',), relation_kwargs=('by',), role_kwargs=('within',), required_value_kwargs=('offset',), edge_kwargs=('edge',), optional_kwargs=('by',), with_relation=('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 #

Amount #

Bases: NamedTuple

What the errors of an operator that steps along an axis say about the amount it takes.

form instance-attribute #

minimum instance-attribute #

negated instance-attribute #

noun instance-attribute #

varies instance-attribute #

Builtin(usage, dimension_kwargs=(), relation_kwargs=(), role_kwargs=(), dimension_or_role_kwargs=(), edge_kwargs=(), required_value_kwargs=(), optional_kwargs=(), 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)); relation_kwargs name a relation, which carries its own dimensions, so it needs no sibling kwarg; 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.

dimension_kwargs = () class-attribute instance-attribute #

dimension_or_role_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, *, with_relation=False) #

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

A dimension, a relation, a column of it, an edge policy, or a plain value. with_relation says whether the call carries a by=, which is what decides the kind of a :attr:dimension_or_role_kwargs member.

Source code in src/math_spec/operators.py
def kind_of(
    self, kwarg: str, *, with_relation: bool = False
) -> 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, a relation, a column of it, an edge policy, or a plain value.
    *with_relation* says whether the call carries a ``by=``, which is what
    decides the kind of a :attr:`dimension_or_role_kwargs` member.
    """
    if kwarg in self.dimension_or_role_kwargs:
        return 'role' if with_relation else 'dimension'
    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

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)
    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}'
    )