Files
Jyotisha/scripts/d2_public_formula_example_validator.py
T
2026-07-23 12:03:58 +08:00

109 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""Validate published D2 arithmetic examples against declared mapping variants.
This is method evidence only. It does not establish a canonical school, a
same-input external oracle, a wealth interpretation, or predictive accuracy.
"""
from __future__ import annotations
import argparse
import json
from datetime import date
from pathlib import Path
from scripts.divisional_charts_extended import DivisionalChartsCalculator
SIGNS = (
"Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
"Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces",
)
# AstroAvastha publishes these four positions and their D2 sign outputs in its
# method explainer. They are examples of the stated parity rule, not a chart
# fixture or a software-oracle response.
PUBLISHED_EXAMPLES = (
{"planet": "Jupiter", "sign": "Aries", "degree": 12.0, "expected_sign": "Leo"},
{"planet": "Venus", "sign": "Taurus", "degree": 22.5, "expected_sign": "Leo"},
{"planet": "Mars", "sign": "Scorpio", "degree": 8.25, "expected_sign": "Cancer"},
{"planet": "Mercury", "sign": "Gemini", "degree": 17.75, "expected_sign": "Cancer"},
)
SOURCE = {
"publisher": "AstroAvastha",
"url": "https://astroavastha.com/blog/hora-d2-chart/",
"tier": "secondary_method_reference",
"supports": "Published arithmetic examples for the odd/even-sign Leo/Cancer D2 mapping.",
"does_not_support": "Canonical-school selection, software identity, same-input oracle parity, wealth outcomes, or timing.",
}
def local_parashara_d2_sign(sign: str, degree: float) -> str:
"""Return the D2 sign from the project's active divisional-chart calculator."""
sign_index = SIGNS.index(sign)
d2_longitude = DivisionalChartsCalculator()._calculate_d2(sign_index, degree)
return SIGNS[int(d2_longitude // 30.0) % 12]
def jyotishyamitra_sequential_d2_sign(sign: str, degree: float) -> str:
"""Return the sequential 15-degree rule observed in the pinned source."""
absolute_degree = SIGNS.index(sign) * 30.0 + degree
return SIGNS[int(absolute_degree // 15.0) % 12]
def build_report() -> dict:
examples = []
for source_row in PUBLISHED_EXAMPLES:
local_result = local_parashara_d2_sign(source_row["sign"], source_row["degree"])
sequential_result = jyotishyamitra_sequential_d2_sign(source_row["sign"], source_row["degree"])
examples.append({
**source_row,
"local_parashara_result": local_result,
"local_engine_source": "scripts.divisional_charts_extended.DivisionalChartsCalculator._calculate_d2",
"jyotishyamitra_sequential_result": sequential_result,
"local_parashara_matches": local_result == source_row["expected_sign"],
"jyotishyamitra_sequential_matches": sequential_result == source_row["expected_sign"],
})
return {
"scope": "public_secondary_d2_formula_examples",
"generated_on": date.today().isoformat(),
"status": "secondary_formula_examples_support_local_parashara_mapping",
"claim_status": "observation_only",
"consumer_policy": "research_observation_only",
"production_tuning_allowed": False,
"truth_matrix_allowed": False,
"source": SOURCE,
"variants": {
"local_parashara": "Odd signs: 0-<15 Leo, 15-<30 Cancer; even signs invert that order.",
"jyotishyamitra_sequential": "Sequential 15-degree chunks across absolute zodiac, observed in support/mod_divisional.py::hora_from_long.",
},
"examples": examples,
"summary": {
"example_count": len(examples),
"local_parashara_match_count": sum(row["local_parashara_matches"] for row in examples),
"jyotishyamitra_sequential_match_count": sum(row["jyotishyamitra_sequential_matches"] for row in examples),
},
"claim_boundary": (
"These publisher-stated arithmetic examples support the project's declared Parashara-style D2 mapping "
"over the observed sequential alternative for this limited example set. They do not create an external "
"software oracle, settle all Hora traditions, validate a birth chart, or support wealth/timing claims."
),
"remaining_requirements": [
"A same-input, independently reproducible D2 software replay with documented settings.",
"A primary or scholarly source that identifies the intended Hora tradition and boundary convention.",
],
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--output", type=Path, required=True)
args = parser.parse_args()
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(build_report(), ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())