Source code for httk.core.vectors.vector_api
"""
The minimal canonical vector interface shared by all vector backends and views.
"""
import fractions
from abc import ABC, abstractmethod
from typing import Any
# The canonical interchange is exactness-preserving: a (possibly nested) tuple of
# fractions.Fraction, or a bare Fraction for a scalar. Every representation can produce this
# exactly (numpy float64 values ARE binary rationals); only the numpy *view* direction is lossy.
[docs]
type Fractions = fractions.Fraction | tuple[Fractions, ...]
[docs]
class VectorAPI(ABC):
"""
Abstract base class for the canonical vector interface.
It declares the exactness-preserving ``fractions`` accessor (a nested tuple of
:class:`fractions.Fraction`, or a bare Fraction for a scalar) that every vector backend
produces from its own native representation and every vector view builds its presentation
from, together with the ``dim`` shape tuple. This is the single interchange format; there is
no pairwise conversion between backends.
On top of the two abstract accessors it provides the guaranteed float renderings
:meth:`to_floats` and :meth:`to_float`, derived from the ``fractions`` hub — so *whatever*
object the family hands you, ``.to_floats()`` works. (The exact value types
:class:`~httk.core.vectors.fracvector.FracVector` and
:class:`~httk.core.vectors.surdvector.SurdVector` honor the same contract with their own
implementations.)
"""
@property
@abstractmethod
[docs]
def fractions(self) -> Fractions:
raise NotImplementedError
@property
@abstractmethod
[docs]
def dim(self) -> tuple[int, ...]:
raise NotImplementedError
[docs]
def to_floats(self) -> Any:
"""
The value as (possibly nested) plain lists of ``float`` — a bare ``float`` for a scalar.
Derived from the exact ``fractions`` hub; nested lists match the
``numpy.ndarray.tolist()`` convention and are directly JSON-serializable.
"""
def rec(node: Fractions) -> Any:
if isinstance(node, tuple):
return [rec(e) for e in node]
return float(node)
return rec(self.fractions)
[docs]
def to_float(self) -> float:
"""The scalar value as a plain ``float``; raises :class:`TypeError` on a non-scalar."""
value = self.fractions
if isinstance(value, tuple):
raise TypeError(f"to_float: expected a scalar, got shape {self.dim}")
return float(value)