diff --git a/README.md b/README.md index ff98710d..09c66cb8 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue)](https://www.python.org/) -[![Techniques](https://img.shields.io/badge/techniques-74-blueviolet)](references/technique_registry.json) -[![Covered](https://img.shields.io/badge/covered-65-green)](references/technique_registry.json) +[![Techniques](https://img.shields.io/badge/techniques-75-blueviolet)](references/technique_registry.json) +[![Covered](https://img.shields.io/badge/covered-66-green)](references/technique_registry.json) [![Complete](https://img.shields.io/badge/complete-9-brightgreen)](references/technique_registry.json) [![Partial](https://img.shields.io/badge/partial-0-lightgrey)](references/technique_registry.json) @@ -31,7 +31,7 @@ This is a **Vedic (Jyotish) astrology analysis system** designed for deep, auditable full-chart readings. It is NOT a simple ephemeris calculator — it is a multi-stage interpretive pipeline that: 1. **Computes** divisional charts (D1/D9/D10/...) via Swiss Ephemeris -2. **Runs** 74 registered techniques (Dashas, Yogas, Shadbala, Ashtakavarga, Transits...) +2. **Runs** 75 registered techniques (Dashas, Yogas, Shadbala, Ashtakavarga, Transits...) 3. **Routes** the analysis through strict workflow paths depending on question type (career / relationship / wealth / timing) 4. **Audits** every technique used — declaring what was called, what is complete/covered, and which limitations affect confidence 5. **Degrades gracefully** — limitations are labeled, not silently over-promising @@ -45,7 +45,7 @@ This is a **Vedic (Jyotish) astrology analysis system** designed for deep, audit | Technique Audit Table (confidence labeling) | ✅ | ❌ | ❌ | ❌ | | Capability degradation (limits are explicit) | ✅ | ❌ | ❌ | ❌ | | MEVG external verification gates | ✅ | ❌ | ❌ | ❌ | -| 74 techniques registered | ✅ | ✅ (50+) | ✅ (200+) | ✅ | +| 75 techniques registered | ✅ | ✅ (50+) | ✅ (200+) | ✅ | | Traditional algorithm benchmarked | ✅ mixed depth | ✅ | ✅ | ✅ | | Docker / MCP Server | ✅ | ❌ | ✅ | ❌ | | English docs / PyPI package | ✅ in progress | ✅ | ✅ | ✅ | diff --git a/references/technique_registry.json b/references/technique_registry.json index ce137dd6..92b44e7d 100644 --- a/references/technique_registry.json +++ b/references/technique_registry.json @@ -878,6 +878,32 @@ "audit_label": "Upapada Lagna", "missing_impact": "Marriage quality analysis incomplete without UL." }, + "special_lagnas_extended": { + "name": "Special Lagnas / HL GL VL PP ViL", + "domains": [ + "lagna", + "timing", + "wealth", + "status" + ], + "status": "covered", + "knowledge_refs": [ + "references/open_source_sources/jaimini-tropical/jaimini/core/lagnas.py" + ], + "commands": [ + "chart", + "full-reading" + ], + "output_paths": [ + "special_lagnas.HL", + "special_lagnas.GL", + "special_lagnas.VL", + "special_lagnas.PP", + "special_lagnas.ViL" + ], + "audit_label": "Special Lagnas", + "missing_impact": "Special Lagna timing, status and prosperity sub-angles are thinner without HL/GL/VL/PP/ViL." + }, "tithi_lord": { "name": "Tithi Lord / 月日守护星", "domains": [ diff --git a/scripts/jaimini.py b/scripts/jaimini.py index 92718bda..fe53ff0a 100644 --- a/scripts/jaimini.py +++ b/scripts/jaimini.py @@ -779,6 +779,9 @@ def calc_special_lagnas_precise( hl_idx = ghati_floor % 12 if ghati_floor % 2 else (7 - ghati_floor) % 12 gl_idx = ghati_floor % 12 vl_idx = (asc_sign_idx * 3) % 12 + vl_from_hl_idx = (hl_idx * 3) % 12 + pp_idx = (hl_idx + gl_idx) % 12 + vil_idx = int((ghatis * 2)) % 12 sunrise_local_hours = (sunrise_utc + tz_offset) % 24 sunrise_local_minutes = int(round(sunrise_local_hours * 60)) % (24 * 60) @@ -794,6 +797,8 @@ def calc_special_lagnas_precise( 'ghatis_elapsed_from_sunrise': round(ghatis, 4), 'HL': _special_lagna_payload('HL', 'Hora Lagna', hl_idx, fraction * 30.0, ghatis, before_sunrise), 'GL': _special_lagna_payload('GL', 'Ghati Lagna', gl_idx, fraction * 30.0, ghatis, before_sunrise), + 'PP': _special_lagna_payload('PP', 'Pranapada Lagna', pp_idx, fraction * 30.0, ghatis, before_sunrise), + 'ViL': _special_lagna_payload('ViL', 'Vighati Lagna', vil_idx, fraction * 30.0, ghatis, before_sunrise), 'VL': { 'name': 'VL', 'full_name': 'Varnada Lagna', @@ -801,8 +806,13 @@ def calc_special_lagnas_precise( 'sign_idx': vl_idx, 'lord': SIGN_LORDS[_sign_name(vl_idx)], 'method': 'Ascendant sign × 3', + 'vl_from_hl': { + 'sign': _sign_name(vl_from_hl_idx), + 'sign_idx': vl_from_hl_idx, + 'lord': SIGN_LORDS[_sign_name(vl_from_hl_idx)], + }, }, - 'note': 'HL/GL以出生地日出为起点计算;GL对24分钟边界敏感,出生时间不准时应结合生时校正。' + 'note': 'HL/GL/PP/ViL以出生地日出为起点计算;GL与ViL对细分钟边界敏感,出生时间不准时应结合生时校正。' } @@ -819,12 +829,22 @@ def calc_special_lagnas(asc_sign_idx: int, hour: int, minute: int = 0) -> Dict: hl_idx = ghati_floor % 12 if ghati_floor % 2 else (7 - ghati_floor) % 12 gl_idx = ghati_floor % 12 vl_idx = (asc_sign_idx * 3) % 12 + vl_from_hl_idx = (hl_idx * 3) % 12 + pp_idx = (hl_idx + gl_idx) % 12 + vil_idx = int(ghatis * 2) % 12 return { 'method': 'Special Lagnas HL/GL/VL simplified (jaimini-tropical MIT adapted; sunrise-sensitive)', 'capability_status': 'auxiliary_partial', 'ghatis_elapsed_from_midnight': round(ghatis, 4), 'HL': {'sign': _sign_name(hl_idx), 'sign_idx': hl_idx, 'lord': SIGN_LORDS[_sign_name(hl_idx)]}, 'GL': {'sign': _sign_name(gl_idx), 'sign_idx': gl_idx, 'lord': SIGN_LORDS[_sign_name(gl_idx)]}, - 'VL': {'sign': _sign_name(vl_idx), 'sign_idx': vl_idx, 'lord': SIGN_LORDS[_sign_name(vl_idx)]}, - 'note': 'HL/GL/VL对日出非常敏感;本函数用于结构化补齐,精确版本需接入当地日出。' + 'PP': {'sign': _sign_name(pp_idx), 'sign_idx': pp_idx, 'lord': SIGN_LORDS[_sign_name(pp_idx)]}, + 'ViL': {'sign': _sign_name(vil_idx), 'sign_idx': vil_idx, 'lord': SIGN_LORDS[_sign_name(vil_idx)]}, + 'VL': { + 'sign': _sign_name(vl_idx), + 'sign_idx': vl_idx, + 'lord': SIGN_LORDS[_sign_name(vl_idx)], + 'vl_from_hl': {'sign': _sign_name(vl_from_hl_idx), 'sign_idx': vl_from_hl_idx, 'lord': SIGN_LORDS[_sign_name(vl_from_hl_idx)]}, + }, + 'note': 'HL/GL/VL/PP/ViL对日出非常敏感;本函数用于结构化补齐,精确版本需接入当地日出。' } diff --git a/tests/test_jaimini.py b/tests/test_jaimini.py index 56e4b296..e49d1b8e 100644 --- a/tests/test_jaimini.py +++ b/tests/test_jaimini.py @@ -150,6 +150,8 @@ class TestSpecialLagnas: assert 'HL' in result assert 'GL' in result assert 'VL' in result + assert 'PP' in result + assert 'ViL' in result def test_ghatis_calculated(self): result = calc_special_lagnas(0, 10, 30) @@ -177,10 +179,24 @@ class TestSpecialLagnas: assert result['precision'] == 'sunrise_correct' assert 'sunrise_local_time' in result assert result['ghatis_elapsed_from_sunrise'] > 0 - for key in ['HL', 'GL', 'VL']: + for key in ['HL', 'GL', 'VL', 'PP', 'ViL']: assert result[key]['sign'] in SIGNS assert result[key]['lord'] in SIGN_LORDS.values() + def test_precise_special_lagnas_include_varnada_crosscheck(self): + result = calc_special_lagnas_precise( + 4, REDACTED_YEAR, 4, 17, 14, 45, lat=36.466667, lon=114.2, tz_offset=8 + ) + assert result['VL']['sign_idx'] == (4 * 3) % 12 + assert 'vl_from_hl' in result['VL'] + + def test_precise_special_lagnas_expose_named_extended_payloads(self): + result = calc_special_lagnas_precise( + 0, 1990, 6, 15, 10, 30, lat=28.6, lon=77.2, tz_offset=5.5 + ) + assert result['PP']['full_name'] == 'Pranapada Lagna' + assert result['ViL']['full_name'] == 'Vighati Lagna' + def test_precise_special_lagnas_are_time_sensitive(self): morning = calc_special_lagnas_precise( 0, 1990, 6, 15, 10, 0, lat=28.6, lon=77.2, tz_offset=5.5