Source code for holovec.encoders.sequence

"""
Sequence encoders for mapping sequences to hypervectors.

This module implements encoders that transform sequences (text, time series,
trajectories) into hypervector representations, preserving order and enabling
partial matching.
"""

from typing import Dict, List, Optional, Union, Tuple
from holovec.encoders.base import SequenceEncoder, ScalarEncoder
from holovec.models.base import VSAModel
from holovec.backends.base import Array


[docs] class PositionBindingEncoder(SequenceEncoder): """ Position binding encoder for sequences using permutation-based positions. Based on Plate (2003) "Holographic Reduced Representations" and Schlegel et al. (2021) "A comparison of vector symbolic architectures". Encodes sequences by binding each element with a position-specific permutation of a base position vector: encode([A, B, C]) = bind(A, ρ¹) + bind(B, ρ²) + bind(C, ρ³) where ρ is the permutation operation and ρⁱ represents i applications. This encoding is: - Order-sensitive: Different positions create different bindings - Variable-length: Works with any sequence length - Partial-match capable: Similar sequences have similar encodings Attributes: codebook: Dictionary mapping symbols to hypervectors auto_generate: Whether to auto-generate vectors for unknown symbols seed_offset: Offset for generating consistent symbol vectors Example: >>> model = VSA.create('MAP', dim=10000) >>> encoder = PositionBindingEncoder(model) >>> >>> # Encode a sequence of symbols >>> seq = ['hello', 'world', '!'] >>> hv = encoder.encode(seq) >>> >>> # Similar sequences have high similarity >>> seq2 = ['hello', 'world'] >>> hv2 = encoder.encode(seq2) >>> model.similarity(hv, hv2) # High (shared prefix) """
[docs] def __init__( self, model: VSAModel, codebook: Optional[Dict[str, Array]] = None, max_length: Optional[int] = None, auto_generate: bool = True, seed: Optional[int] = None ): """ Initialize position binding encoder. Args: model: VSA model instance codebook: Pre-defined symbol → hypervector mapping (optional) max_length: Maximum sequence length (None for unlimited) auto_generate: Auto-generate vectors for unknown symbols (default: True) seed: Random seed for generating symbol vectors Raises: ValueError: If model is not compatible """ super().__init__(model, max_length) self.codebook = codebook if codebook is not None else {} self.auto_generate = auto_generate self.seed = seed self._next_symbol_seed = 0 # Counter for symbol generation
[docs] def encode(self, sequence: List[Union[str, int]]) -> Array: """ Encode sequence using position binding. Each element is bound with a position-specific permutation and all bound pairs are bundled: result = Σᵢ bind(element_i, permute(position_vector, i)) Args: sequence: List of symbols (strings or integers) to encode Returns: Hypervector representing the sequence Raises: ValueError: If sequence is empty ValueError: If sequence exceeds max_length ValueError: If symbol not in codebook and auto_generate=False Example: >>> encoder.encode(['cat', 'sat', 'on', 'mat']) """ if not sequence: raise ValueError("Cannot encode empty sequence") if self.max_length is not None and len(sequence) > self.max_length: raise ValueError( f"Sequence length {len(sequence)} exceeds max_length {self.max_length}" ) # Get or generate hypervectors for each symbol symbol_vectors = [] for symbol in sequence: if symbol not in self.codebook: if not self.auto_generate: raise ValueError( f"Symbol '{symbol}' not in codebook and auto_generate=False" ) # Generate new vector for this symbol self.codebook[symbol] = self._generate_symbol_vector(symbol) symbol_vectors.append(self.codebook[symbol]) # Bind each symbol with its position and bundle position_bound = [] for i, symbol_vec in enumerate(symbol_vectors): # Position encoding: permute by position index # permute(vec, i) applies permutation i times position_vec = self.model.permute(symbol_vec, k=i) position_bound.append(position_vec) # Bundle all position-bound vectors sequence_hv = self.model.bundle(position_bound) return sequence_hv
[docs] def decode( self, hypervector: Array, max_positions: int = 10, threshold: float = 0.3 ) -> List[str]: """ Decode sequence hypervector to recover symbols. Uses cleanup memory approach: for each position, unpermute and find most similar symbol in codebook. Args: hypervector: Sequence hypervector to decode max_positions: Maximum positions to try decoding (default: 10) threshold: Minimum similarity threshold for valid symbols (default: 0.3) Returns: List of decoded symbols (may be shorter than original) Raises: RuntimeError: If codebook is empty Note: Decoding is approximate and works best for sequences shorter than max_positions with high SNR. Example: >>> encoded = encoder.encode(['a', 'b', 'c']) >>> decoded = encoder.decode(encoded, max_positions=5) >>> decoded # ['a', 'b', 'c'] (approximate) """ if not self.codebook: raise RuntimeError("Cannot decode: codebook is empty") # Convert codebook to symbol → vector for faster lookup symbols = list(self.codebook.keys()) vectors = [self.codebook[s] for s in symbols] decoded = [] for pos in range(max_positions): # Unpermute by position to recover symbol at this position unpermuted = self.model.unpermute(hypervector, k=pos) # Find most similar symbol in codebook best_similarity = -float('inf') best_symbol = None for symbol, symbol_vec in zip(symbols, vectors): sim = float(self.model.similarity(unpermuted, symbol_vec)) if sim > best_similarity: best_similarity = sim best_symbol = symbol # Only include if above threshold if best_similarity >= threshold: decoded.append(best_symbol) else: # No strong match - likely end of sequence break return decoded
def _generate_symbol_vector(self, symbol: Union[str, int]) -> Array: """ Generate a random hypervector for a new symbol. Uses consistent seeding based on symbol to ensure reproducibility. Args: symbol: Symbol to generate vector for Returns: Random hypervector for this symbol """ # Create seed from base seed + symbol hash + counter if self.seed is not None: symbol_seed = self.seed + hash(symbol) % 10000 + self._next_symbol_seed else: symbol_seed = hash(symbol) % 100000 + self._next_symbol_seed self._next_symbol_seed += 1 return self.model.random(seed=symbol_seed)
[docs] def add_symbol(self, symbol: Union[str, int], vector: Optional[Array] = None): """ Add a symbol to the codebook. Args: symbol: Symbol to add vector: Hypervector to associate (generated if None) Example: >>> # Pre-define a vector for a special symbol >>> special_vec = model.random(seed=42) >>> encoder.add_symbol('<START>', special_vec) """ if vector is None: vector = self._generate_symbol_vector(symbol) self.codebook[symbol] = vector
[docs] def get_codebook_size(self) -> int: """ Get number of symbols in codebook. Returns: Number of symbols stored """ return len(self.codebook)
@property def is_reversible(self) -> bool: """ PositionBindingEncoder supports approximate decoding. Returns: True (approximate decoding available) """ return True @property def compatible_models(self) -> List[str]: """ Works with all VSA models that support permutation. Returns: List of all model names """ return ["MAP", "FHRR", "HRR", "BSC", "GHRR", "VTB", "BSDC"]
[docs] def __repr__(self) -> str: """String representation.""" return ( f"PositionBindingEncoder(" f"model={self.model.model_name}, " f"codebook_size={len(self.codebook)}, " f"max_length={self.max_length}, " f"auto_generate={self.auto_generate})" )
[docs] class NGramEncoder(SequenceEncoder): """ N-gram encoder for capturing local sequence patterns using sliding windows. Based on Plate (2003), Rachkovskij (1996), and Kleyko et al. (2023) Section 3.3.4. Encodes sequences by extracting n-grams (sliding windows of n consecutive symbols) and encoding each n-gram compositionally: For sequence [A, B, C, D] with n=2, stride=1: - Extract n-grams: [A,B], [B,C], [C,D] - Encode each n-gram using position binding - Combine via bundling or chaining Two encoding modes: 1. **Bundling mode** (bag-of-ngrams): encode(seq) = bundle([encode_ngram([A,B]), encode_ngram([B,C]), ...]) - Order-invariant across n-grams (but preserves within n-gram) - Good for classification (e.g., text categorization) - Similar to bag-of-words but with local context 2. **Chaining mode** (ordered n-grams): encode(seq) = Σᵢ bind(encode_ngram(ngramᵢ), ρⁱ) - Order-sensitive across n-grams - Good for sequence matching - Enables partial decoding Attributes: n: Size of n-grams (1=unigrams, 2=bigrams, 3=trigrams, etc.) stride: Step size between n-grams (1=overlapping, n=non-overlapping) mode: 'bundling' or 'chaining' ngram_encoder: Internal PositionBindingEncoder for individual n-grams Example: >>> model = VSA.create('MAP', dim=10000) >>> encoder = NGramEncoder(model, n=2, stride=1, mode='bundling') >>> >>> # Encode text as bigrams >>> seq = ['the', 'cat', 'sat', 'on', 'mat'] >>> hv = encoder.encode(seq) # Bigrams: [the,cat], [cat,sat], [sat,on], [on,mat] >>> >>> # Similar text has high similarity >>> seq2 = ['the', 'cat', 'sat', 'on', 'hat'] >>> hv2 = encoder.encode(seq2) # Shares 3/4 bigrams >>> model.similarity(hv, hv2) # High similarity """
[docs] def __init__( self, model: VSAModel, n: int = 2, stride: int = 1, mode: str = 'bundling', codebook: Optional[Dict[str, Array]] = None, auto_generate: bool = True, seed: Optional[int] = None ): """ Initialize n-gram encoder. Args: model: VSA model instance n: Size of n-grams (must be >= 1) stride: Step between n-grams (must be >= 1) mode: 'bundling' for bag-of-ngrams or 'chaining' for ordered n-grams codebook: Optional pre-defined symbol → hypervector mapping auto_generate: Auto-generate vectors for unknown symbols seed: Random seed for symbol vector generation Raises: ValueError: If n < 1, stride < 1, or mode is invalid """ super().__init__(model, max_length=None) if n < 1: raise ValueError(f"n must be >= 1, got {n}") if stride < 1: raise ValueError(f"stride must be >= 1, got {stride}") if mode not in ['bundling', 'chaining']: raise ValueError(f"mode must be 'bundling' or 'chaining', got '{mode}'") self.n = n self.stride = stride self.mode = mode # Internal encoder for individual n-grams # Each n-gram is encoded as a position-bound sequence self.ngram_encoder = PositionBindingEncoder( model=model, codebook=codebook, max_length=n, # Each n-gram has length n auto_generate=auto_generate, seed=seed )
[docs] def encode(self, sequence: List[Union[str, int]]) -> Array: """ Encode sequence using n-gram representation. Extracts all n-grams using sliding window with specified stride, encodes each n-gram, then combines via bundling or chaining. Args: sequence: List of symbols to encode Returns: Hypervector representing the sequence as n-grams Raises: ValueError: If sequence is too short (length < n) Example: >>> # Bigrams with stride=1 (overlapping) >>> encoder = NGramEncoder(model, n=2, stride=1) >>> encoder.encode(['A', 'B', 'C']) # N-grams: AB, BC >>> >>> # Trigrams with stride=2 (partial overlap) >>> encoder = NGramEncoder(model, n=3, stride=2) >>> encoder.encode(['A', 'B', 'C', 'D', 'E']) # N-grams: ABC, CDE """ if len(sequence) < self.n: raise ValueError( f"Sequence length {len(sequence)} is less than n={self.n}" ) # Extract all n-grams using sliding window ngrams = [] for i in range(0, len(sequence) - self.n + 1, self.stride): ngram = sequence[i:i + self.n] ngrams.append(ngram) if not ngrams: raise ValueError("No n-grams extracted from sequence") # Encode each n-gram using position binding ngram_hvs = [] for ngram in ngrams: ngram_hv = self.ngram_encoder.encode(ngram) ngram_hvs.append(ngram_hv) # Combine n-gram hypervectors based on mode if self.mode == 'bundling': # Bag-of-ngrams: simple bundle (order-invariant) sequence_hv = self.model.bundle(ngram_hvs) else: # mode == 'chaining' # Ordered n-grams: bind each with position position_bound = [] for i, ngram_hv in enumerate(ngram_hvs): # Position encoding: permute by n-gram index position_hv = self.model.permute(ngram_hv, k=i) position_bound.append(position_hv) sequence_hv = self.model.bundle(position_bound) return sequence_hv
[docs] def decode( self, hypervector: Array, max_ngrams: int = 10, threshold: float = 0.3 ) -> List[List[Union[str, int]]]: """ Decode n-gram hypervector to recover n-grams. Only supported for 'chaining' mode. For 'bundling' mode, n-grams are order-invariant and cannot be sequentially decoded. Args: hypervector: Encoded sequence hypervector max_ngrams: Maximum number of n-grams to decode threshold: Minimum similarity threshold for valid n-grams Returns: List of decoded n-grams, each as a list of symbols Raises: NotImplementedError: If mode is 'bundling' (not decodable) RuntimeError: If codebook is empty Example: >>> encoder = NGramEncoder(model, n=2, mode='chaining') >>> hv = encoder.encode(['A', 'B', 'C']) >>> decoder.decode(hv, max_ngrams=3) # [['A', 'B'], ['B', 'C']] """ if self.mode != 'chaining': raise NotImplementedError( f"Decoding only supported for 'chaining' mode, not '{self.mode}'" ) if not self.ngram_encoder.codebook: raise RuntimeError("Cannot decode: codebook is empty") # For chaining mode, unpermute each position and decode the n-gram decoded_ngrams = [] for pos in range(max_ngrams): # Unpermute by position to recover n-gram at this index unpermuted = self.model.unpermute(hypervector, k=pos) # Decode the n-gram using ngram_encoder try: ngram_symbols = self.ngram_encoder.decode( unpermuted, max_positions=self.n, threshold=threshold ) # Only include if we got a full n-gram if len(ngram_symbols) >= self.n: decoded_ngrams.append(ngram_symbols[:self.n]) else: # Incomplete n-gram - likely end of sequence break except Exception: # Decoding failed - likely end of sequence break return decoded_ngrams
[docs] def get_codebook(self) -> Dict[str, Array]: """ Get the internal symbol codebook. Returns: Dictionary mapping symbols to hypervectors """ return self.ngram_encoder.codebook
[docs] def get_codebook_size(self) -> int: """ Get number of unique symbols in codebook. Returns: Number of symbols """ return self.ngram_encoder.get_codebook_size()
@property def is_reversible(self) -> bool: """ NGramEncoder supports decoding only in 'chaining' mode. Returns: True if mode is 'chaining', False if 'bundling' """ return self.mode == 'chaining' @property def compatible_models(self) -> List[str]: """ Works with all VSA models. Returns: List of all model names """ return ["MAP", "FHRR", "HRR", "BSC", "GHRR", "VTB", "BSDC"]
[docs] def __repr__(self) -> str: """String representation.""" return ( f"NGramEncoder(" f"model={self.model.model_name}, " f"n={self.n}, " f"stride={self.stride}, " f"mode='{self.mode}', " f"codebook_size={self.get_codebook_size()})" )
[docs] class TrajectoryEncoder(SequenceEncoder): """ Trajectory encoder for continuous sequences (time series, paths, motion). Based on Frady et al. (2021) "Computing on Functions" and position binding from Plate (2003), encoding trajectories by binding temporal information with spatial positions. A trajectory is a sequence of positions over time: - 1D: time series [v₁, v₂, v₃, ...] - 2D: path [(x₁,y₁), (x₂,y₂), ...] - 3D: motion [(x₁,y₁,z₁), (x₂,y₂,z₂), ...] Encoding strategy: For each time step tᵢ with position pᵢ: 1. Encode time: time_hv = scalar_encode(tᵢ) 2. Encode position coords: coord_hvs = [scalar_encode(c) for c in pᵢ] 3. Bind coords to dimensions: pos_hv = Σⱼ bind(Dⱼ, coord_hv_j) 4. Bind time with position: point_hv = bind(time_hv, pos_hv) 5. Permute by index: indexed_hv = permute(point_hv, i) trajectory_hv = Σᵢ indexed_hv This creates an encoding that: - Preserves temporal ordering (via permutation) - Captures smooth trajectories (via continuous scalar encoding) - Enables partial matching and interpolation - Supports multi-dimensional paths Attributes: scalar_encoder: Encoder for continuous values (FPE or Thermometer) n_dimensions: Dimensionality of trajectory (1D, 2D, or 3D) time_range: (min_time, max_time) for temporal normalization dim_vectors: Hypervectors for spatial dimensions (x, y, z) Example: >>> from holovec import VSA >>> from holovec.encoders import FractionalPowerEncoder, TrajectoryEncoder >>> >>> model = VSA.create('FHRR', dim=10000) >>> scalar_enc = FractionalPowerEncoder(model, min_val=0, max_val=100) >>> encoder = TrajectoryEncoder(model, scalar_encoder=scalar_enc, n_dimensions=2) >>> >>> # Encode a 2D path >>> path = [(10, 20), (15, 25), (20, 30), (25, 35)] >>> hv = encoder.encode(path) >>> >>> # Similar paths have high similarity >>> path2 = [(10, 20), (15, 25), (20, 30), (25, 40)] # Slightly different >>> hv2 = encoder.encode(path2) >>> model.similarity(hv, hv2) # High similarity """
[docs] def __init__( self, model: VSAModel, scalar_encoder: ScalarEncoder, n_dimensions: int = 1, time_range: Optional[Tuple[float, float]] = None, seed: Optional[int] = None ): """ Initialize trajectory encoder. Args: model: VSA model instance scalar_encoder: Encoder for continuous values (FPE or Thermometer recommended) n_dimensions: Trajectory dimensionality (1, 2, or 3) time_range: (min, max) time values for normalization (optional) seed: Random seed for dimension vector generation Raises: ValueError: If n_dimensions not in {1, 2, 3} TypeError: If scalar_encoder is not reversible """ super().__init__(model, max_length=None) if n_dimensions not in {1, 2, 3}: raise ValueError( f"n_dimensions must be 1, 2, or 3, got {n_dimensions}" ) if not isinstance(scalar_encoder, ScalarEncoder): raise TypeError( f"scalar_encoder must be a ScalarEncoder, got {type(scalar_encoder)}" ) # Check model compatibility if model != scalar_encoder.model: raise ValueError( "scalar_encoder must use the same VSA model as TrajectoryEncoder" ) self.scalar_encoder = scalar_encoder self.n_dimensions = n_dimensions self.time_range = time_range self.seed = seed # Generate dimension hypervectors (for x, y, z coordinates) self.dim_vectors: List[Array] = [] for i in range(n_dimensions): dim_seed = (seed + i) if seed is not None else (1000 + i) self.dim_vectors.append(model.random(seed=dim_seed))
[docs] def encode(self, trajectory: List[Union[float, Tuple[float, ...]]]) -> Array: """ Encode a trajectory as a hypervector. Each point in the trajectory is encoded with temporal information, then all points are combined with position-based permutation. Args: trajectory: List of points - 1D: List[float] e.g., [1.0, 2.5, 3.7, ...] - 2D: List[Tuple[float, float]] e.g., [(1,2), (3,4), ...] - 3D: List[Tuple[float, float, float]] e.g., [(1,2,3), ...] Returns: Hypervector representing the trajectory Raises: ValueError: If trajectory is empty or points have wrong dimensionality Example: >>> # 1D time series >>> encoder_1d = TrajectoryEncoder(model, scalar_enc, n_dimensions=1) >>> hv = encoder_1d.encode([1.0, 2.5, 3.7, 5.2]) >>> >>> # 2D path >>> encoder_2d = TrajectoryEncoder(model, scalar_enc, n_dimensions=2) >>> hv = encoder_2d.encode([(0,0), (1,1), (2,2)]) """ if len(trajectory) == 0: raise ValueError("Cannot encode empty trajectory") # Encode each point with temporal binding point_hvs = [] for i, point in enumerate(trajectory): # Normalize point to tuple format if self.n_dimensions == 1: # 1D: scalar → (scalar,) if isinstance(point, (int, float)): coords = (float(point),) else: coords = (float(point[0]),) else: # 2D/3D: accept tuple, list, or array-like try: # Convert to tuple (works for tuple, list, numpy array, etc.) coords = tuple(float(c) for c in point) except (TypeError, ValueError): raise ValueError( f"Expected iterable for {self.n_dimensions}D point, got {type(point)}" ) # Validate dimensionality if len(coords) != self.n_dimensions: raise ValueError( f"Expected {self.n_dimensions}D point, got {len(coords)}D: {coords}" ) # Encode time (index as time if no time_range specified) if self.time_range is not None: # Normalize time to range t = i / len(trajectory) # [0, 1] t_scaled = self.time_range[0] + t * (self.time_range[1] - self.time_range[0]) time_hv = self.scalar_encoder.encode(t_scaled) else: # Use index directly time_hv = self.scalar_encoder.encode(float(i)) # Encode position (bind each coordinate with its dimension) coord_hvs = [] for j, coord_val in enumerate(coords): coord_hv = self.scalar_encoder.encode(coord_val) dim_hv = self.dim_vectors[j] bound_coord = self.model.bind(dim_hv, coord_hv) coord_hvs.append(bound_coord) # Bundle coordinates to create position hypervector pos_hv = self.model.bundle(coord_hvs) # Bind time with position point_hv = self.model.bind(time_hv, pos_hv) # Apply position-specific permutation (for ordering) indexed_hv = self.model.permute(point_hv, k=i) point_hvs.append(indexed_hv) # Bundle all points trajectory_hv = self.model.bundle(point_hvs) return trajectory_hv
[docs] def decode(self, hypervector: Array, max_points: int = 10) -> List[Tuple[float, ...]]: """ Decode trajectory hypervector to recover approximate points. Note: Trajectory decoding is not yet implemented. It requires: 1. Unpermuting each position 2. Unbinding time from position 3. Unbinding each coordinate from dimension vectors 4. Decoding scalar values 5. Interpolation for smooth trajectories Args: hypervector: Encoded trajectory hypervector max_points: Maximum points to decode Returns: List of decoded points (not implemented yet) Raises ------ NotImplementedError Trajectory decoding requires solving nested binding inverse problem. Notes ----- Trajectory decoding is not implemented because it requires multi-level unbinding with cascading error accumulation: **Mathematical Challenge:** The encoding process creates nested bindings: trajectory_hv = bundle([ bind(time(t), bind(dimension(d), scalar(coord[t,d]))) for all t, d ]) To decode a single point at time t: 1. Unbind time: point_hv[t] = unbind(trajectory_hv, time(t)) 2. For each dimension d: a. Unbind dimension: coord_hv[d] = unbind(point_hv[t], dimension(d)) b. Decode scalar: coord[t,d] = scalar_decode(coord_hv[d]) **Why This Is Intractable:** - **Two-level unbinding**: Time then dimension (or vice versa) - **Error compounding**: Each unbind adds noise - **No known time points**: Must search over possible time values - **Interpolation complexity**: Smooth trajectory requires dense sampling - **Computational cost**: * For T time points, D dimensions * Requires: T × D × (decode_iterations) evaluations * Example: 100 points × 3D × 100 iterations = 30,000 evals **Additional Challenges:** 1. **Order Ambiguity**: Don't know which time point comes first 2. **Density Unknown**: Don't know temporal sampling rate 3. **Dimension Count**: Must know dimensionality a priori 4. **Coordinate Ranges**: Scalar decoder needs value bounds **Possible Approaches (Future Work):** 1. **Constrained Decoding**: If time points are known: - Unbind each known time point - Decode coordinates independently - Complexity: O(T × D × decode_cost) 2. **Template Matching**: Pre-encode common trajectory patterns - Create codebook of canonical trajectories - Use cleanup to find nearest match - Works for classification, not reconstruction 3. **Learned Decoder**: Train neural network trajectory_hv → points - Requires large training dataset - Can learn to handle noise and ambiguity - See: Imani et al. (2019) for similar approach 4. **Iterative Resonator**: Use resonator cleanup at each level - Unbind time with resonator cleanup - Unbind dimension with resonator cleanup - Requires codebooks for both time and coordinates **Current Recommendation:** Use TrajectoryEncoder for one-way encoding in applications like: - Trajectory classification (gesture recognition, motion analysis) - Trajectory similarity search (find similar paths) - Trajectory clustering (group similar motions) For reconstruction, consider storing original trajectories separately and using hypervector encoding only for similarity queries. References ---------- - Plate (2003): "Holographic Reduced Representations" - Section 4.3 on error accumulation in multi-level binding - Räsänen & Saarinen (2016): "Sequence prediction with sparse distributed hyperdimensional coding" - Analysis of temporal binding """ raise NotImplementedError( "Trajectory decoding is not implemented due to nested binding complexity. " "See docstring for detailed mathematical explanation. " "For reconstruction tasks, store original trajectories and use " "hypervector encoding for similarity-based retrieval only." )
@property def is_reversible(self) -> bool: """ TrajectoryEncoder does not yet support decoding. Returns: False (decoding not implemented) Note: Decoding requires multi-level unbinding and interpolation, which will be implemented in a future version. """ return False @property def compatible_models(self) -> List[str]: """ Works with all VSA models. Returns: List of all model names """ return ["MAP", "FHRR", "HRR", "BSC", "GHRR", "VTB", "BSDC"] @property def input_type(self) -> str: """Input type description.""" dim_names = {1: "1D time series", 2: "2D path", 3: "3D trajectory"} return dim_names[self.n_dimensions]
[docs] def __repr__(self) -> str: """String representation.""" return ( f"TrajectoryEncoder(" f"model={self.model.model_name}, " f"scalar_encoder={type(self.scalar_encoder).__name__}, " f"n_dimensions={self.n_dimensions}, " f"time_range={self.time_range})" )