Source code for pyhdc.encodings.holographic

#!/usr/bin/env python
"""
Holographic Encodings for HDC

HDC-compatible wrapper for HRR and FHRR encodings.
"""

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

import numpy as np

from pyhdc.components.binding import (
    CircularConvolution,
    CircularCorrelation,
    ElementAngleAddition,
    ElementAngleSubtraction,
)
from pyhdc.components.bundling import (
    AnglesOfElementAddition,
    ElementAddition,
    ElementAdditionConstantNormalized,
    ElementAdditionNormalized,
)
from pyhdc.components.elements import NormalReal, UniformAngles
from pyhdc.components.similarity import AngleDistance, CosineSimilarity
from pyhdc.components.thinning import NoThin
from pyhdc.components.unary import (
    L2Normalize,
    Negate,
    PhaseNegate,
    ReverseInverse,
    WrapPhase,
)
from pyhdc.encodings.base import Encoding
from pyhdc.generation.base import HDCGenerator
from pyhdc.hypervector import EncodingSpec
from pyhdc.types import Backend, Device

# ============================================================================
# Holographic Encodings
# ============================================================================


[docs] class HRR(Encoding): r""" Holographic Reduced Representation. Uses circular convolution for binding and normalized bundling. Args: random_choice_range: Optional float (rho). When set, coordinates whose \|pre-norm sum\| <= rho * sqrt(N) are replaced by independent N(0,1) draws before normalization (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( ElementAdditionNormalized, random_choice_range=self._random_choice_range ) else: bundling_fn = ElementAdditionNormalized return EncodingSpec( dtype=np.float32, element_generator=NormalReal, similarity_fn=CosineSimilarity, bundling_fn=bundling_fn, thinning_fn=NoThin, binding_fn=CircularConvolution, unbinding_fn=CircularCorrelation, generator_output_type="floats", inverse_fn=ReverseInverse, normalize_fn=L2Normalize, negative_fn=Negate, )
[docs] class HRR_NoNorm(Encoding): """HRR without normalized bundling.""" def _get_encoding_spec(self) -> EncodingSpec: return EncodingSpec( dtype=np.float32, element_generator=NormalReal, similarity_fn=CosineSimilarity, bundling_fn=ElementAddition, thinning_fn=NoThin, binding_fn=CircularConvolution, unbinding_fn=CircularCorrelation, generator_output_type="floats", inverse_fn=ReverseInverse, normalize_fn=L2Normalize, negative_fn=Negate, )
[docs] class HRR_ConstNorm(Encoding): """HRR with constant normalization for bundling.""" def _get_encoding_spec(self) -> EncodingSpec: return EncodingSpec( dtype=np.float32, element_generator=NormalReal, similarity_fn=CosineSimilarity, bundling_fn=ElementAdditionConstantNormalized, thinning_fn=NoThin, binding_fn=CircularConvolution, unbinding_fn=CircularCorrelation, generator_output_type="floats", inverse_fn=ReverseInverse, normalize_fn=L2Normalize, negative_fn=Negate, )
[docs] class FHRR(Encoding): """ Fourier Holographic Reduced Representation. Uses angular/phase representations with angle-based operations. Args: random_choice_range: Optional float (rho). When set, coordinates whose phasor magnitude <= rho * sqrt(N/2) are replaced by independent Uniform[-pi, pi] draws (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( AnglesOfElementAddition, random_choice_range=self._random_choice_range ) else: bundling_fn = AnglesOfElementAddition return EncodingSpec( dtype=np.float32, element_generator=UniformAngles, similarity_fn=AngleDistance, bundling_fn=bundling_fn, thinning_fn=NoThin, binding_fn=ElementAngleAddition, unbinding_fn=ElementAngleSubtraction, generator_output_type="floats", inverse_fn=PhaseNegate, normalize_fn=WrapPhase, )