Source code for pyhdc.encodings.map

#!/usr/bin/env python
"""
Multiply-Add-Permute Encodings for HDC

HDC-compatible wrapper for MAP encodings.
"""

from functools import partial
from typing import Any, Callable, Optional

import numpy as np

from pyhdc.components.binding import ElementMultiplication
from pyhdc.components.bundling import (
    ElementAddition,
    ElementAdditionBipolarThreshold,
    ElementAdditionBits,
    ElementAdditionCut,
)
from pyhdc.components.elements import BernoulliBiploar, UniformBipolar
from pyhdc.components.similarity import CosineSimilarity
from pyhdc.components.thinning import NoThin
from pyhdc.encodings.base import Encoding
from pyhdc.generation.base import HDCGenerator
from pyhdc.hypervector import EncodingSpec
from pyhdc.types import Backend, Device

# ============================================================================
# Multiply-Add-Permute Encodings
# ============================================================================


[docs] class MAP_C(Encoding): """ Multiply-Add-Permute encoding with continuous values. Uses bipolar values with element-wise multiplication for binding and cosine similarity for comparison. Args: random_choice_range: Optional float (rho). When set, coordinates whose |pre-aggregate sum| <= rho * sqrt(N/3) are replaced by independent Uniform[-1,1] draws during bundling (band randomization). """ def __init__( self, dimension: int = 10_000, backend: Optional[Backend] = None, device: Optional[Device] = None, dtype: Optional[Any] = None, mask: Optional[int] = None, generator: Optional[HDCGenerator] = None, similarity_remap: Optional[Callable] = None, random_choice_range: Optional[float] = None, ) -> None: self._random_choice_range = random_choice_range super().__init__( dimension, backend, device, dtype, mask, generator, similarity_remap ) def _get_encoding_spec(self) -> EncodingSpec: if self._random_choice_range is not None: bundling_fn = partial( ElementAdditionCut, random_choice_range=self._random_choice_range ) else: bundling_fn = ElementAdditionCut return EncodingSpec( dtype=np.float32, element_generator=UniformBipolar, similarity_fn=CosineSimilarity, bundling_fn=bundling_fn, thinning_fn=NoThin, binding_fn=ElementMultiplication, unbinding_fn=ElementMultiplication, generator_output_type="floats", )
[docs] class MAP_I(Encoding): """ Multiply-Add-Permute encoding with integer values. Uses bipolar integer values with element-wise multiplication for binding and cosine similarity for comparison. Args: random_choice_range: Optional float (rho). When set, coordinates whose |pre-aggregate sum| <= rho * sqrt(N) are replaced by independent {-1, +1} draws during bundling (band randomization). """ def __init__( self, dimension: int = 10_000, backend: Optional[Backend] = None, device: Optional[Device] = None, dtype: Optional[Any] = None, mask: Optional[int] = None, generator: Optional[HDCGenerator] = None, similarity_remap: Optional[Callable] = None, random_choice_range: Optional[float] = None, ) -> None: self._random_choice_range = random_choice_range super().__init__( dimension, backend, device, dtype, mask, generator, similarity_remap ) def _get_encoding_spec(self) -> EncodingSpec: if self._random_choice_range is not None: bundling_fn = partial( ElementAddition, random_choice_range=self._random_choice_range ) else: bundling_fn = ElementAddition return EncodingSpec( dtype=np.int32, element_generator=BernoulliBiploar, similarity_fn=CosineSimilarity, bundling_fn=bundling_fn, thinning_fn=NoThin, binding_fn=ElementMultiplication, unbinding_fn=ElementMultiplication, generator_output_type="bits", )
[docs] class MAP_I_Bits(Encoding): """ Multiply-Add-Permute encoding with bit-limited integer values. Similar to MAP_I but with configurable bit limits via mask parameter. """ def __init__( self, dimension: int = 10_000, backend: Optional[Backend] = None, device: Optional[Device] = None, dtype: Optional[Any] = None, mask: int = (2**32) - 1, generator: Optional[HDCGenerator] = None, similarity_remap: Optional[Callable] = None, ) -> None: self._mask = mask super().__init__( dimension, backend, device, dtype, mask, generator, similarity_remap ) def _get_encoding_spec(self) -> EncodingSpec: bundling_fn = partial( ElementAdditionBits, min_val=np.iinfo(np.int32).min, max_val=np.iinfo(np.int32).max, ) return EncodingSpec( dtype=np.int32, element_generator=BernoulliBiploar, similarity_fn=CosineSimilarity, bundling_fn=bundling_fn, thinning_fn=NoThin, binding_fn=ElementMultiplication, unbinding_fn=ElementMultiplication, mask=self._mask, generator_output_type="words", )
[docs] class MAP_B(Encoding): """ Multiply-Add-Permute with bipolar thresholding. Uses bipolar values with thresholding during bundling. Args: random_choice_range: Optional float (rho). When set, coordinates whose |bipolar sum| <= rho * sqrt(N) are replaced by independent {-1, +1} draws during bundling (band randomization). """ def __init__( self, dimension: int = 10_000, backend: Optional[Backend] = None, device: Optional[Device] = None, dtype: Optional[Any] = None, mask: Optional[int] = None, generator: Optional[HDCGenerator] = None, similarity_remap: Optional[Callable] = None, random_choice_range: Optional[float] = None, ) -> None: self._random_choice_range = random_choice_range super().__init__( dimension, backend, device, dtype, mask, generator, similarity_remap ) def _get_encoding_spec(self) -> EncodingSpec: if self._random_choice_range is not None: bundling_fn = partial( ElementAdditionBipolarThreshold, random_choice_range=self._random_choice_range, ) else: bundling_fn = ElementAdditionBipolarThreshold return EncodingSpec( dtype=np.int8, element_generator=BernoulliBiploar, similarity_fn=CosineSimilarity, bundling_fn=bundling_fn, thinning_fn=NoThin, binding_fn=ElementMultiplication, unbinding_fn=ElementMultiplication, generator_output_type="bits", )