Source code for stellium.analysis.vector
"""Generate embedding vectors to represent individual charts for fast comparison."""
import numpy as np
from stellium.core.models import CalculatedChart
from stellium.core.registry import get_object_info
# Define the standard bodies to guarantee vector alignment
VECTOR_BODIES = [
"Sun",
"Moon",
"Mercury",
"Venus",
"Mars",
"Jupiter",
"Saturn",
"Uranus",
"Neptune",
"Pluto",
"True Node",
]
[docs]
class ChartVectorizer:
"""Transforms a stellium CalculatedChart into a dense vector embedding."""
def __init__(self, include_speed: bool = True, include_houses: bool = True):
self.include_speed = include_speed
self.include_houses = include_houses
# Calculate expected dimension (sanity check)
self.dim = len(VECTOR_BODIES) * (3 if include_speed else 2) + 4 # 4 angles
if include_houses:
self.dim += 24 # sin, cos of position = * 2
[docs]
def encode(self, chart: CalculatedChart) -> np.ndarray:
features = []
# Body encoding
for name in VECTOR_BODIES:
obj = chart.get_object(name)
# Cyclic encoding (0..1 range not needed, -1..1 is better for ML)
rads = np.deg2rad(obj.longitude)
features.extend([np.sin(rads), np.cos(rads)])
if self.include_speed:
avg_daily_motion = get_object_info(name).avg_daily_motion
norm_speed = obj.speed / avg_daily_motion
features.append(norm_speed)
# Angles (AC, MC) - Important for house matching
for angle in ["AC", "MC"]:
obj = chart.get_object(angle)
rads = np.deg2rad(obj.longitude)
features.extend([np.sin(rads), np.cos(rads)])
# House cusps (optional)
if self.include_houses:
cusps = chart.get_houses().cusps
for cusp in cusps:
rads = np.deg2rad(cusp)
features.extend([np.sin(rads), np.cos(rads)])
return np.array(features, dtype=np.float32)
[docs]
def similarity(self, vec_a: np.ndarray, vec_b: np.ndarray) -> float:
"""Cosine similarity between two charts."""
norm_a = np.linalg.norm(vec_a)
norm_b = np.linalg.norm(vec_b)
return np.dot(vec_a, vec_b) / (norm_a * norm_b)