Source code for pyhdc.generation.lfsr

#!/usr/bin/env python
"""
Linear Feedback Shift Register for HDC

HDC-compatible wrapper for LFSR random number generation.
"""

import random
from typing import Any, Dict, List, Optional

from pyhdc.generation.base import HDCGenerator


class LFSRGenerator(HDCGenerator):
    """
    Linear Feedback Shift Register for hypervector generation.

    Base class for LFSR-based generators with bit-level operations.
    """

    def __init__(
        self,
        width: int = 32,
        taps: Optional[List[int]] = None,
        seed: Optional[int] = None,
        implementation: str = "fibonacci",
    ) -> None:
        """
        Initialize LFSR generator.

        Args:
            width: The width of the LFSR register in bits
            taps: List of tap positions for feedback
            seed: Optional seed for reproducibility
            implementation: Type of LFSR ('fibonacci' or 'galois')
        """
        self._width = width
        self._taps = taps if taps is not None else [width - 1, width - 2]
        self._implementation = implementation
        self._validate_parameters()
        super().__init__(seed)

    def _validate_parameters(self) -> None:
        """Validate LFSR parameters."""
        if not isinstance(self._width, int) or self._width <= 0:
            raise ValueError("Width must be a positive integer")

        if not isinstance(self._taps, list) or not all(
            isinstance(t, int) for t in self._taps
        ):
            raise ValueError("Taps must be a list of integers")

        if not all(0 <= t < self._width for t in self._taps):
            raise ValueError(f"Taps must be between 0 and {self._width - 1}")

        if len(self._taps) == 0:
            raise ValueError("At least one tap position must be specified")

    def _configure_internal(self) -> None:
        """Configure the LFSR state."""
        self._max_value = (1 << self._width) - 1

        if self._seed is None:
            self._state = random.randint(1, self._max_value)
        else:
            if (
                not isinstance(self._seed, int)
                or self._seed <= 0
                or self._seed > self._max_value
            ):
                raise ValueError(f"Seed must be between 1 and {self._max_value}")
            self._state = self._seed

        self._initial_state = self._state
        self._taps_sorted = sorted(list(set(self._taps)), reverse=True)

    def _next_bit_fibonacci(self) -> int:
        """Generate next bit using Fibonacci configuration."""
        feedback_bit = 0
        for tap in self._taps_sorted:
            feedback_bit ^= (self._state >> tap) & 1

        output_bit = self._state & 1
        self._state = (self._state >> 1) | (feedback_bit << (self._width - 1))

        return output_bit

    def _next_bit_galois(self) -> int:
        """Generate next bit using Galois configuration."""
        output_bit = self._state & 1
        self._state >>= 1

        if output_bit:
            for tap in self._taps_sorted:
                self._state ^= 1 << tap

        return output_bit

    def _next_bit(self) -> int:
        """Generate next bit."""
        if self._implementation == "fibonacci":
            return self._next_bit_fibonacci()
        elif self._implementation == "galois":
            return self._next_bit_galois()
        else:
            raise ValueError(f"Unknown implementation: {self._implementation}")

    def _next_word(self, word_size: int = 32) -> int:
        """Generate next word by collecting bits."""
        word = 0
        for i in range(word_size):
            word |= self._next_bit() << i
        return word

    def set_parameters(
        self,
        width: Optional[int] = None,
        taps: Optional[List[int]] = None,
        seed: Optional[int] = None,
        implementation: Optional[str] = None,
    ) -> None:
        """
        Set LFSR parameters.

        Args:
            width: The width of the LFSR register in bits
            taps: List of tap positions
            seed: The seed value
            implementation: Type of LFSR
        """
        if width is not None:
            self._width = width

        if taps is not None:
            self._taps = taps

        if implementation is not None:
            if implementation not in ["fibonacci", "galois"]:
                raise ValueError("Implementation must be 'fibonacci' or 'galois'")
            self._implementation = implementation

        if seed is not None:
            self._seed = seed

        self._validate_parameters()
        self._configure_internal()

    def set_width(self, width: int) -> None:
        """Set the width parameter."""
        self.set_parameters(width=width)

    def set_taps(self, taps: List[int]) -> None:
        """Set the tap positions."""
        self.set_parameters(taps=taps)

    def set_implementation(self, implementation: str) -> None:
        """Set the LFSR implementation type."""
        self.set_parameters(implementation=implementation)

    def get_parameters(self) -> Dict[str, Any]:
        """Get current LFSR parameters."""
        return {
            "width": self._width,
            "taps": self._taps.copy(),
            "seed": self._seed,
            "implementation": self._implementation,
        }

    def get_width(self) -> int:
        """Get the width parameter."""
        return self._width

    def get_taps(self) -> List[int]:
        """Get the tap positions."""
        return self._taps.copy()

    def get_implementation(self) -> str:
        """Get the implementation type."""
        return self._implementation

    def reset(self) -> None:
        """Reset to initial state."""
        self._state = self._initial_state

    def get_state(self) -> int:
        """Get current state."""
        return self._state


[docs] class FibonacciLFSRGenerator(LFSRGenerator): """Fibonacci-style LFSR generator.""" def __init__( self, width: int = 32, taps: Optional[List[int]] = None, seed: Optional[int] = None, ) -> None: """Initialize Fibonacci LFSR.""" super().__init__(width, taps, seed, "fibonacci")
[docs] class GaloisLFSRGenerator(LFSRGenerator): """Galois-style LFSR generator.""" def __init__( self, width: int = 32, taps: Optional[List[int]] = None, seed: Optional[int] = None, ) -> None: """Initialize Galois LFSR.""" super().__init__(width, taps, seed, "galois")
# Predefined LFSR configurations
[docs] class CommonLFSRGenerators: """Factory for common LFSR configurations.""" @staticmethod def fibonacci_8(seed: Optional[int] = None) -> FibonacciLFSRGenerator: """Standard 8-bit Fibonacci LFSR.""" return FibonacciLFSRGenerator(width=8, taps=[7, 5, 4, 3], seed=seed) @staticmethod def fibonacci_16(seed: Optional[int] = None) -> FibonacciLFSRGenerator: """Standard 16-bit Fibonacci LFSR.""" return FibonacciLFSRGenerator(width=16, taps=[15, 14, 12, 3], seed=seed) @staticmethod def fibonacci_32(seed: Optional[int] = None) -> FibonacciLFSRGenerator: """Standard 32-bit Fibonacci LFSR.""" return FibonacciLFSRGenerator(width=32, taps=[31, 21, 1, 0], seed=seed) @staticmethod def galois_16(seed: Optional[int] = None) -> GaloisLFSRGenerator: """Standard 16-bit Galois LFSR.""" return GaloisLFSRGenerator(width=16, taps=[15, 14, 12, 3], seed=seed) @staticmethod def galois_32(seed: Optional[int] = None) -> GaloisLFSRGenerator: """Standard 32-bit Galois LFSR.""" return GaloisLFSRGenerator(width=32, taps=[31, 21, 1, 0], seed=seed) @staticmethod def maximal_64(seed: Optional[int] = None) -> FibonacciLFSRGenerator: """64-bit LFSR with maximal period taps.""" return FibonacciLFSRGenerator(width=64, taps=[63, 62, 60, 59], seed=seed)
# Legacy alias for backward compatibility LFSR = FibonacciLFSRGenerator