Files
Jyotisha/scripts/tajika_kernel.py
T
ClaudeandClaude Opus 5.5 2bbf984017 fix(tajika): port the nine-yoga Tajika authority and instantaneous applying test from yinduzhanxing (BUG-1652)
The product still ran the 2026-09-03 two-yoga version, so Nakta, Yamaya,
Manahoo and Kamboola showed as blocked. Port yinduzhanxing fb8fe915: the
governed draft and authority (nine yogas), the kernel's applying/separating
by instantaneous relative speed, the rule record (author corrected to
P.V.R. Narasimha Rao in this copy) and the upstream tests. In the annual
year-chart layer every Tajika connection is marked source_review: pending;
only Ithasala / Easarapha still produce factors until the rules are reviewed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbBXoyn7Hj3LCW3BUs5bZ8
2026-11-06 15:23:17 +08:00

112 lines
4.5 KiB
Python

"""Strict seven-planet Tajika aspect kernel.
This module deliberately exposes only the auditable interaction layer. Named
Tajika chains remain blocked until their classical definitions have golden
cases; it never treats nodes as Tajika planets.
"""
from __future__ import annotations
from typing import Any
SEVEN_PLANETS = ("Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn")
DEEPTAMSA = {"Sun": 15.0, "Moon": 12.0, "Mars": 8.0, "Mercury": 7.0, "Jupiter": 9.0, "Venus": 7.0, "Saturn": 9.0}
ASPECT_ANGLES = (0.0, 60.0, 90.0, 120.0, 180.0)
def _signed_angle(value: float) -> float:
return (value + 180.0) % 360.0 - 180.0
def _nearest_aspect(delta: float) -> tuple[float, float]:
candidates = []
for aspect in ASPECT_ANGLES:
for target in ({0.0} if aspect in (0.0, 180.0) else {aspect, -aspect}):
candidates.append((target, _signed_angle(delta - target)))
return min(candidates, key=lambda item: abs(item[1]))
def evaluate_tajika_pair(
left: str,
right: str,
planets: dict[str, dict[str, Any]],
) -> dict[str, Any] | None:
"""Return the shared motion/orb contract for one seven-planet Tajika pair."""
if left not in planets or right not in planets:
return None
left_payload = planets[left]
right_payload = planets[right]
if not isinstance(left_payload, dict) or not isinstance(right_payload, dict):
return None
if "longitude" not in left_payload or "longitude" not in right_payload:
return None
if "speed" not in left_payload or "speed" not in right_payload:
return None
left_lon, right_lon = float(left_payload["longitude"]) % 360, float(right_payload["longitude"]) % 360
aspect, residual = _nearest_aspect(right_lon - left_lon)
orb = (DEEPTAMSA[left] + DEEPTAMSA[right]) / 2.0
if abs(residual) > orb:
return None
relative_speed = float(right_payload["speed"]) - float(left_payload["speed"])
# Ported from yinduzhanxing fb8fe915 (BUG-1652). Applying/separating is an
# instantaneous relation. A full-day step makes fast bodies (especially the
# Moon) leap over an exact aspect and reverses a still-applying relation.
# The signed residual approaches zero iff its derivative, the relative
# speed, has the opposite sign.
applying = residual * relative_speed < 0.0
return {
"planets": [left, right],
"aspect": abs(aspect),
"residual": round(residual, 6),
"average_deeptamsa": orb,
"motion": "applying" if applying else "separating",
"within_deeptamsa": True,
}
def calculate_tajika_interactions(planets: dict[str, dict[str, Any]]) -> dict[str, Any]:
missing = [planet for planet in SEVEN_PLANETS if planet not in planets or "longitude" not in planets[planet] or "speed" not in planets[planet]]
if missing:
return {
"scope": "tajika_seven_planet_kernel",
"status": "blocked",
"reason": "longitude_and_speed_required_for_all_seven_planets",
"missing": missing,
"nodes_excluded": True,
}
interactions = []
for index, left in enumerate(SEVEN_PLANETS):
for right in SEVEN_PLANETS[index + 1:]:
pair = evaluate_tajika_pair(left, right, planets)
if pair is not None:
interactions.append(pair)
return {
"scope": "tajika_seven_planet_kernel",
"status": "partial",
"nodes_excluded": True,
"interactions": interactions,
"candidate_yogas": [
{
"name": "Ithasala_candidate" if row["motion"] == "applying" else "Easarapha_candidate",
"planets": row["planets"],
"aspect": row["aspect"],
"residual": row["residual"],
"average_deeptamsa": row["average_deeptamsa"],
"motion": row["motion"],
"rule_source": "references/tajika-yoga-complete-guide.md#2.1-2.2",
"status": "partial",
}
for row in interactions
],
"supported_named_yogas": ["Ithasala", "Easarapha"],
"blocked_named_yogas": ["Nakta", "Yamaya", "Manahoo", "Kamboola"],
"authority_lane": "scripts/tajika_named_yoga_authority.py",
"boundary": (
"Seven-planet aspect, Deeptamsa, and applying/separating evidence are "
"admissible for governed Ithasala/Easarapha authority. Broader named-yoga "
"chains and event verdicts remain blocked pending classic golden cases."
),
}