class BaseTensorStats(TensorStats):
"""
Base class for tensor statistics.
"""
def __init__(
self,
index_order: Iterable[Field] | BaseTensorStats,
dim_sizes: MutableMapping[Field, float] | None = None,
fill_value: Any = None,
):
if isinstance(index_order, BaseTensorStats):
if dim_sizes is not None:
raise TypeError(
"dim_sizes must be omitted when constructing from BaseTensorStats"
)
self._init_from_fields(
index_order.index_order, index_order.dim_sizes, index_order.fill_value
)
else:
if dim_sizes is None:
raise TypeError("dim_sizes are required when constructing from fields")
self._init_from_fields(index_order, dim_sizes, fill_value)
def _init_from_fields(
self,
index_order: Iterable[Field],
dim_sizes: MutableMapping[Field, float],
fill_value: Any,
):
self._index_order = tuple(index_order)
self._dim_sizes: OrderedDict[Field, float] = OrderedDict(dim_sizes)
# Stats always carry an AbstractFill: propagation must be able to say
# "this fill exists but must not be specialized on".
self._fill_value: AbstractFill = as_fill(fill_value)
@classmethod
def from_fields(
cls,
index_order: Iterable[Field],
dim_sizes: MutableMapping[Field, float],
fill_value: Any,
) -> Self:
return cls(index_order, dim_sizes, fill_value)
def copy(self) -> Self:
new = object.__new__(type(self))
new.__dict__ = {name: copy.copy(value) for name, value in self.__dict__.items()}
return new
@property
def dim_sizes(self) -> MutableMapping[Field, float]:
return self._dim_sizes
@dim_sizes.setter
def dim_sizes(self, value: MutableMapping[Field, float]):
self._dim_sizes = OrderedDict(value)
def get_dim_size(self, idx: Field) -> float:
return self.dim_sizes[idx]
@property
def index_order(self) -> tuple[Field, ...]:
return self._index_order
@index_order.setter
def index_order(self, value: Iterable[Field]):
self._index_order = tuple(value)
@property
def idxs(self) -> tuple[Field, ...]:
return self.index_order
@property
def fill_value(self) -> AbstractFill:
"""The fill, and whether kernels may specialize on it."""
return self._fill_value
@fill_value.setter
def fill_value(self, value: Any):
self._fill_value = as_fill(value)
def get_dim_space_size(self, idx: Iterable[Field]) -> float:
prod = 1
for i in idx:
prod *= int(self.dim_sizes[i])
if prod == 0 or prod > np.iinfo(np.int64).max:
return float("inf")
return float(prod)