fix(saham): apply Table 74 before legacy rows (BUG-1474, upstream cb8a1ca7)
Directed-arc sign stays as BUG-1387. formula_traces remains the 36-row report list. Book rows are no longer overwritten by the old house approximation. Co-Authored-By: Grok (agent loop)
This commit is contained in:
co-authored by
Grok (agent loop)
parent
9d3f59a872
commit
16bae00615
+126
-5
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"source": "PVR Integrated Approach, section 28.8.1, Table 74, source pages 394-395",
|
||||
"house_operand_profile": "pvr_equal_30_degrees_from_ascendant",
|
||||
"house_operand_source": "PVR Example 121, page 396: house longitude retains the Ascendant degree in successive signs; use the traditional sign lord",
|
||||
"description": "Tajika Saham Formulas. Format: P1 - P2 + P3. If birth is at night, the formula often flips to P2 - P1 + P3 (depending on the specific Saham rule).",
|
||||
"sahams": {
|
||||
"Punya_Saham": {
|
||||
@@ -22,8 +25,8 @@
|
||||
},
|
||||
"Mitra_Saham": {
|
||||
"name_en": "Friends",
|
||||
"formula_day": ["Jupiter", "Venus", "Ascendant"],
|
||||
"formula_night": ["Venus", "Jupiter", "Ascendant"],
|
||||
"formula_day": ["Jupiter", "Punya_Saham", "Venus"],
|
||||
"formula_night": ["Punya_Saham", "Jupiter", "Venus"],
|
||||
"description": "Indicates friends and social circle."
|
||||
},
|
||||
"Mahatmya_Saham": {
|
||||
@@ -34,10 +37,128 @@
|
||||
},
|
||||
"Gaurava_Saham": {
|
||||
"name_en": "Respect",
|
||||
"formula_day": ["Jupiter", "Moon", "Ascendant"],
|
||||
"formula_night": ["Moon", "Jupiter", "Ascendant"],
|
||||
"formula_day": ["Jupiter", "Moon", "Sun"],
|
||||
"formula_night": ["Moon", "Jupiter", "Sun"],
|
||||
"description": "Indicates respect and honor in society."
|
||||
},
|
||||
"Vivaha_Saham": {
|
||||
"formula_day": ["Venus", "Saturn", "Ascendant"],
|
||||
"formula_night": ["Saturn", "Venus", "Ascendant"]
|
||||
},
|
||||
"Rajya_Saham": {
|
||||
"formula_day": ["Saturn", "Sun", "Ascendant"],
|
||||
"formula_night": ["Sun", "Saturn", "Ascendant"]
|
||||
},
|
||||
"Satru_Saham": {
|
||||
"formula_day": ["Mars", "Saturn", "Ascendant"],
|
||||
"formula_night": ["Saturn", "Mars", "Ascendant"]
|
||||
},
|
||||
"Putra_Saham": {
|
||||
"formula_day": ["Jupiter", "Moon", "Ascendant"],
|
||||
"formula_night": ["Moon", "Jupiter", "Ascendant"]
|
||||
},
|
||||
"Bandhu_Saham": {
|
||||
"formula_day": ["Mercury", "Moon", "Ascendant"],
|
||||
"formula_night": ["Moon", "Mercury", "Ascendant"]
|
||||
},
|
||||
"Matri_Saham": {
|
||||
"formula_day": ["Moon", "Venus", "Ascendant"],
|
||||
"formula_night": ["Venus", "Moon", "Ascendant"]
|
||||
},
|
||||
"Pitri_Saham": {
|
||||
"formula_day": ["Saturn", "Sun", "Ascendant"],
|
||||
"formula_night": ["Sun", "Saturn", "Ascendant"]
|
||||
},
|
||||
"Bhratri_Saham": {
|
||||
"formula_day": ["Jupiter", "Saturn", "Ascendant"],
|
||||
"formula_night": ["Jupiter", "Saturn", "Ascendant"]
|
||||
},
|
||||
"Roga_Saham": {
|
||||
"formula_day": ["Ascendant", "Moon", "Ascendant"],
|
||||
"formula_night": ["Moon", "Ascendant", "Ascendant"]
|
||||
},
|
||||
"Mrityu_Saham": {
|
||||
"formula_day": ["House8Cusp", "Moon", "Ascendant"],
|
||||
"formula_night": ["House8Cusp", "Moon", "Ascendant"]
|
||||
},
|
||||
"Paradesa_Saham": {
|
||||
"formula_day": ["House9Cusp", "House9Lord", "Ascendant"],
|
||||
"formula_night": ["House9Cusp", "House9Lord", "Ascendant"]
|
||||
},
|
||||
"Artha_Saham": {
|
||||
"formula_day": ["House2Cusp", "House2Lord", "Ascendant"],
|
||||
"formula_night": ["House2Cusp", "House2Lord", "Ascendant"]
|
||||
},
|
||||
"Labha_Saham": {
|
||||
"formula_day": ["House11Cusp", "House11Lord", "Ascendant"],
|
||||
"formula_night": ["House11Cusp", "House11Lord", "Ascendant"]
|
||||
},
|
||||
"Asha_Saham": {
|
||||
"formula_day": ["Saturn", "Mars", "Ascendant"],
|
||||
"formula_night": ["Mars", "Saturn", "Ascendant"]
|
||||
},
|
||||
"Samartha_Saham": {
|
||||
"formula_day": ["Mars", "House1Lord", "Ascendant"],
|
||||
"formula_night": ["House1Lord", "Mars", "Ascendant"],
|
||||
"mars_lagna_formula_day": ["Jupiter", "Mars", "Ascendant"],
|
||||
"mars_lagna_formula_night": ["Mars", "Jupiter", "Ascendant"]
|
||||
},
|
||||
"Jeeva_Saham": {
|
||||
"formula_day": ["Saturn", "Jupiter", "Ascendant"],
|
||||
"formula_night": ["Jupiter", "Saturn", "Ascendant"]
|
||||
},
|
||||
"Kali_Saham": {
|
||||
"formula_day": ["Jupiter", "Mars", "Ascendant"],
|
||||
"formula_night": ["Mars", "Jupiter", "Ascendant"]
|
||||
},
|
||||
"Sastra_Saham": {
|
||||
"formula_day": ["Jupiter", "Saturn", "Mercury"],
|
||||
"formula_night": ["Saturn", "Jupiter", "Mercury"]
|
||||
},
|
||||
"Paradara_Saham": {
|
||||
"formula_day": ["Venus", "Sun", "Ascendant"],
|
||||
"formula_night": ["Sun", "Venus", "Ascendant"]
|
||||
},
|
||||
"Vanik_Saham": {
|
||||
"formula_day": ["Moon", "Mercury", "Ascendant"],
|
||||
"formula_night": ["Mercury", "Moon", "Ascendant"]
|
||||
},
|
||||
"Karyasiddhi_Saham": {
|
||||
"formula_day": ["Saturn", "Sun", "SunSignLord"],
|
||||
"formula_night": ["Saturn", "Moon", "MoonSignLord"]
|
||||
},
|
||||
"Santapa_Saham": {
|
||||
"formula_day": ["Saturn", "Moon", "House6Cusp"],
|
||||
"formula_night": ["Moon", "Saturn", "House6Cusp"]
|
||||
},
|
||||
"Sraddha_Saham": {
|
||||
"formula_day": ["Venus", "Mars", "Ascendant"],
|
||||
"formula_night": ["Mars", "Venus", "Ascendant"]
|
||||
},
|
||||
"Preeti_Saham": {
|
||||
"formula_day": ["Sastra_Saham", "Punya_Saham", "Ascendant"],
|
||||
"formula_night": ["Punya_Saham", "Sastra_Saham", "Ascendant"]
|
||||
},
|
||||
"Jadya_Saham": {
|
||||
"formula_day": ["Mars", "Saturn", "Mercury"],
|
||||
"formula_night": ["Saturn", "Mars", "Mercury"]
|
||||
},
|
||||
"Vyapara_Saham": {
|
||||
"formula_day": ["Mars", "Saturn", "Ascendant"],
|
||||
"formula_night": ["Mars", "Saturn", "Ascendant"]
|
||||
},
|
||||
"Jalapatana_Saham": {
|
||||
"formula_day": ["Cancer15", "Saturn", "Ascendant"],
|
||||
"formula_night": ["Saturn", "Cancer15", "Ascendant"]
|
||||
},
|
||||
"Bandhana_Saham": {
|
||||
"formula_day": ["Punya_Saham", "Saturn", "Ascendant"],
|
||||
"formula_night": ["Saturn", "Punya_Saham", "Ascendant"]
|
||||
},
|
||||
"Apamrityu_Saham": {
|
||||
"formula_day": ["House8Cusp", "Mars", "Ascendant"],
|
||||
"formula_night": ["Mars", "House8Cusp", "Ascendant"]
|
||||
},
|
||||
"Karma_Saham": {
|
||||
"name_en": "Action / Profession",
|
||||
"formula_day": ["Mars", "Mercury", "Ascendant"],
|
||||
@@ -46,6 +167,6 @@
|
||||
}
|
||||
},
|
||||
"exception_rules": {
|
||||
"add_30_degrees": "For A - B + C, add 30 degrees when C does not lie on the forward zodiacal arc from B to A. PVR Integrated Approach section 28.8.1, source page 394."
|
||||
"add_30_degrees": "For A - B + C, add 30 degrees if C does not lie on the forward zodiacal arc from B to A. C is the actual third operand, not necessarily Ascendant."
|
||||
}
|
||||
}
|
||||
|
||||
+112
-122
@@ -76,6 +76,22 @@ _PANCHAVARGIYA_RULES = _load_panchavargiya_rules()
|
||||
def _resolve_saham_operand(operand: str, planet_lons: Dict[str, float], asc_lon: float, computed: Dict[str, float]) -> float:
|
||||
if operand == 'Ascendant':
|
||||
return float(asc_lon) % 360
|
||||
if operand == 'Cancer15':
|
||||
return 105.0
|
||||
if operand in ('SunSignLord', 'MoonSignLord'):
|
||||
luminary = operand.removesuffix('SignLord')
|
||||
sign = SIGNS[int((float(planet_lons[luminary]) % 360) // 30)]
|
||||
return float(planet_lons[SIGN_LORDS[sign]]) % 360
|
||||
if operand.startswith('House') and operand.endswith(('Cusp', 'Lord')):
|
||||
suffix = 'Cusp' if operand.endswith('Cusp') else 'Lord'
|
||||
house = int(operand[5:-len(suffix)])
|
||||
if not 1 <= house <= 12:
|
||||
raise ValueError('Saham house must be 1..12')
|
||||
# PVR Example 121: equal-house cusps keep the Ascendant's degree.
|
||||
cusp = (float(asc_lon) + 30 * (house - 1)) % 360
|
||||
if suffix == 'Cusp':
|
||||
return cusp
|
||||
return float(planet_lons[SIGN_LORDS[SIGNS[int(cusp // 30)]]]) % 360
|
||||
if operand in computed:
|
||||
return float(computed[operand]) % 360
|
||||
if operand in planet_lons:
|
||||
@@ -83,6 +99,15 @@ def _resolve_saham_operand(operand: str, planet_lons: Dict[str, float], asc_lon:
|
||||
raise KeyError(f"Unsupported saham operand: {operand}")
|
||||
|
||||
|
||||
def _select_saham_formula(rule_name: str, asc_lon: float, is_day: bool) -> List:
|
||||
rule = _SAHAM_RULES['sahams'][rule_name]
|
||||
variant = 'day' if is_day else 'night'
|
||||
if (rule_name == 'Samartha_Saham'
|
||||
and SIGN_LORDS[SIGNS[int((float(asc_lon) % 360) // 30)]] == 'Mars'):
|
||||
return rule['mars_lagna_formula_' + variant]
|
||||
return rule['formula_' + variant]
|
||||
|
||||
|
||||
def _calc_formula_saham(
|
||||
rule_name: str,
|
||||
planet_lons: Dict[str, float],
|
||||
@@ -90,13 +115,13 @@ def _calc_formula_saham(
|
||||
is_day: bool,
|
||||
computed: Dict[str, float],
|
||||
) -> float:
|
||||
rule = _SAHAM_RULES['sahams'][rule_name]
|
||||
formula = rule['formula_day'] if is_day else rule['formula_night']
|
||||
formula = _select_saham_formula(rule_name, asc_lon, is_day)
|
||||
first = _resolve_saham_operand(formula[0], planet_lons, asc_lon, computed)
|
||||
second = _resolve_saham_operand(formula[1], planet_lons, asc_lon, computed)
|
||||
third = _resolve_saham_operand(formula[2], planet_lons, asc_lon, computed)
|
||||
result = (third + (first - second)) % 360
|
||||
# PVR §28.8.1, p394: in A-B+C, C must lie on the forward arc B -> A.
|
||||
# Direction matches BUG-1387 (ce9fed15): do not flip it back.
|
||||
third_between_points = (third - second) % 360 <= (first - second) % 360
|
||||
return result if third_between_points else (result + 30.0) % 360
|
||||
|
||||
@@ -109,15 +134,14 @@ def _formula_saham_trace(
|
||||
computed: Dict[str, float],
|
||||
) -> Dict:
|
||||
"""Calculate a rule-backed Saham while preserving its sensitive convention."""
|
||||
rule = _SAHAM_RULES['sahams'][rule_name]
|
||||
formula = rule['formula_day'] if is_day else rule['formula_night']
|
||||
formula = _select_saham_formula(rule_name, asc_lon, is_day)
|
||||
first = _resolve_saham_operand(formula[0], planet_lons, asc_lon, computed)
|
||||
second = _resolve_saham_operand(formula[1], planet_lons, asc_lon, computed)
|
||||
third = _resolve_saham_operand(formula[2], planet_lons, asc_lon, computed)
|
||||
base_longitude = (third + (first - second)) % 360
|
||||
third_between_points = (third - second) % 360 <= (first - second) % 360
|
||||
exception_applied = not third_between_points
|
||||
return {
|
||||
trace = {
|
||||
'longitude': (base_longitude + 30.0) % 360 if exception_applied else base_longitude,
|
||||
'base_longitude': base_longitude,
|
||||
'rule_name': rule_name,
|
||||
@@ -126,6 +150,9 @@ def _formula_saham_trace(
|
||||
'zodiacal_order_exception_applied': exception_applied,
|
||||
'rule_source': 'references/saham_rules.json',
|
||||
}
|
||||
if any(str(operand).startswith('House') for operand in formula):
|
||||
trace['house_operand_profile'] = _SAHAM_RULES['house_operand_profile']
|
||||
return trace
|
||||
|
||||
|
||||
def calc_muntha(birth_asc_idx: int, age: int) -> Dict:
|
||||
@@ -1627,199 +1654,147 @@ def calc_all_sahams(planet_lons: Dict[str, float],
|
||||
|
||||
is_day = daynight['is_daytime']
|
||||
|
||||
results = {'status': 'partial', 'daynight_evidence': daynight}
|
||||
results = {
|
||||
'status': 'partial',
|
||||
'daynight_evidence': daynight,
|
||||
'formula_rule_source': 'references/saham_rules.json',
|
||||
'formula_rule_boundary': (
|
||||
'All 36 rows of PVR Table 74 are calculated under its declared day/night '
|
||||
'and directed-arc rules. Additional legacy rows remain separate candidates, '
|
||||
'not established classical formulas or vendor equivalents.'
|
||||
),
|
||||
}
|
||||
computed_formula_sahams: Dict[str, float] = {}
|
||||
formula_meta: Dict[str, Dict] = {}
|
||||
# Table 74 rows. Legacy approximations below must not overwrite these keys.
|
||||
for rule_name, result_key, label, interpretation in (
|
||||
('Punya_Saham', 'punya_saham', 'Punya Saham', '福德点——善业、精神成长、父系福德'),
|
||||
('Vidya_Saham', 'vidya_saham', 'Vidya Saham', '学识点——学习、智力、教育'),
|
||||
('Yashas_Saham', 'yasha_saham', 'Yasha Saham', '名声点——声誉、社会地位'),
|
||||
('Mitra_Saham', 'mitra_saham', 'Mitra Saham', '友伴点——朋友、社交圈'),
|
||||
('Mahatmya_Saham', 'mahatmya_saham', 'Mahatmya Saham', '卓越点——成就、影响力'),
|
||||
('Gaurava_Saham', 'gaurava_saham', 'Gaurava Saham', '尊荣点——尊重、社会声望'),
|
||||
('Karma_Saham', 'karma_saham', 'Karma Saham', '行动点——职业、行动与责任'),
|
||||
('Vivaha_Saham', 'vivah_saham', 'Vivaha Saham', '婚姻点'),
|
||||
('Rajya_Saham', 'rajya_saham', 'Rajya Saham', '王权点'),
|
||||
('Satru_Saham', 'shatru_saham', 'Satru Saham', '敌人点'),
|
||||
('Putra_Saham', 'putra_saham', 'Putra Saham', '子女点'),
|
||||
('Bandhu_Saham', 'bandhu_saham', 'Bandhu Saham', '亲属点'),
|
||||
('Matri_Saham', 'matri_saham', 'Matri Saham', '母亲点'),
|
||||
('Pitri_Saham', 'pitri_saham', 'Pitri Saham', '父亲点'),
|
||||
('Bhratri_Saham', 'bhraatri_saham', 'Bhratri Saham', '兄弟姐妹点'),
|
||||
('Roga_Saham', 'rogha_saham', 'Roga Saham', '疾病点'),
|
||||
('Mrityu_Saham', 'mrityu_saham', 'Mrityu Saham', '终结点'),
|
||||
('Paradesa_Saham', 'paradesa_saham', 'Paradesa Saham', '海外点'),
|
||||
('Artha_Saham', 'artha_saham', 'Artha Saham', '财富点'),
|
||||
('Labha_Saham', 'labha_saham', 'Labha Saham', '收益点'),
|
||||
('Asha_Saham', 'asha_saham', 'Asha Saham', '愿望点'),
|
||||
('Samartha_Saham', 'samartha_saham', 'Samartha Saham', '能力与事业开拓点'),
|
||||
('Jeeva_Saham', 'jeeva_saham', 'Jeeva Saham', '生命点'),
|
||||
('Kali_Saham', 'kali_saham', 'Kali Saham', '传统逆境点'),
|
||||
('Sastra_Saham', 'sastra_saham', 'Sastra Saham', '学科点;不同于旧候选Shastra武器点'),
|
||||
('Paradara_Saham', 'paradara_saham', 'Paradara Saham', '传统婚外关系象征点'),
|
||||
('Vanik_Saham', 'vanik_saham', 'Vanik Saham', '商业贸易点'),
|
||||
('Karyasiddhi_Saham', 'karyasiddhi_saham', 'Karyasiddhi Saham', '事务达成点'),
|
||||
('Santapa_Saham', 'santapa_saham', 'Santapa Saham', '忧虑点'),
|
||||
('Sraddha_Saham', 'sraddha_saham', 'Sraddha Saham', '虔诚与诚意点'),
|
||||
('Preeti_Saham', 'preeti_saham', 'Preeti Saham', '情感依恋点'),
|
||||
('Jadya_Saham', 'jadya_saham', 'Jadya Saham', '传统慢性病象征点'),
|
||||
('Vyapara_Saham', 'vyapara_saham', 'Vyapara Saham', '经营点'),
|
||||
('Jalapatana_Saham', 'jalapatana_saham', 'Jalapatana Saham', '渡海点'),
|
||||
('Bandhana_Saham', 'bandhana_saham', 'Bandhana Saham', '束缚点'),
|
||||
('Apamrityu_Saham', 'apamrityu_saham', 'Apamrityu Saham', '传统意外终结象征点'),
|
||||
):
|
||||
trace = _formula_saham_trace(rule_name, planet_lons, asc_lon, is_day, computed_formula_sahams)
|
||||
computed_formula_sahams[rule_name] = trace['longitude']
|
||||
formula_meta[result_key] = {
|
||||
'rule_name': trace.get('rule_name'),
|
||||
'formula_operands': trace.get('formula_operands'),
|
||||
'formula_variant': trace.get('formula_variant'),
|
||||
'zodiacal_order_exception_applied': trace.get('zodiacal_order_exception_applied'),
|
||||
}
|
||||
results[result_key] = _saham_dict(trace['longitude'], label, interpretation, trace=trace)
|
||||
|
||||
# 1. Punya Saham(福德点):Moon - Sun + Asc
|
||||
p_lon = _calc_formula_saham('Punya_Saham', planet_lons, asc_lon, is_day, computed_formula_sahams)
|
||||
computed_formula_sahams['Punya_Saham'] = p_lon
|
||||
results['punya_saham'] = _saham_dict(p_lon, 'Punya Saham', '福德点——善业、精神成长、父系福德')
|
||||
|
||||
# 2. Karya Saham(事业点):
|
||||
# 日间:Mars - Moon + Asc;夜间:Moon - Mars + Asc
|
||||
# Legacy candidates that are not Table 74 names. Shastra stays distinct from Sastra.
|
||||
if is_day:
|
||||
k_lon = _saham(moon_lon, mars_lon)
|
||||
else:
|
||||
k_lon = _saham(mars_lon, moon_lon)
|
||||
results['karya_saham'] = _saham_dict(k_lon, 'Karya Saham', '事业点——行动、努力、工作成果')
|
||||
|
||||
# 3. Vivah Saham(婚姻点):
|
||||
# 日间:Venus - Mars + Asc(男性)或 Mars - Venus + Asc(女性)
|
||||
# 简化:用Venus - Mars
|
||||
v_lon = _saham(mars_lon, venus_lon)
|
||||
results['vivah_saham'] = _saham_dict(v_lon, 'Vivah Saham', '婚姻点——婚姻、伴侣、情感关系')
|
||||
|
||||
# 4. Rajya Saham(王权点):Sun - Jupiter + Asc
|
||||
r_lon = _saham(jupiter_lon, sun_lon)
|
||||
results['rajya_saham'] = _saham_dict(r_lon, 'Rajya Saham', '王权点——地位、权威、社会认可')
|
||||
|
||||
# 5. Shatru Saham(敌人点):Mars - Saturn + Asc
|
||||
sh_lon = _saham(saturn_lon, mars_lon)
|
||||
results['shatru_saham'] = _saham_dict(sh_lon, 'Shatru Saham', '敌人点——竞争对手、障碍、冲突来源')
|
||||
|
||||
# 6. Labha Saham(收益点):Jupiter - Moon + Asc
|
||||
l_lon = _saham(moon_lon, jupiter_lon)
|
||||
results['labha_saham'] = _saham_dict(l_lon, 'Labha Saham', '收益点——收获、增益、扩张')
|
||||
|
||||
# 7. Parakrama Saham(勇气点):Mars - Sun + Asc
|
||||
pa_lon = _saham(sun_lon, mars_lon)
|
||||
results['parakrama_saham'] = _saham_dict(pa_lon, 'Parakrama Saham', '勇气点——勇气、竞争力、克服困难')
|
||||
|
||||
# 8-36. 扩展Sahams(Tajika系统36种)
|
||||
# 8. Putra Saham(子女点):Jupiter - Moon + Asc
|
||||
putra_lon = _saham(moon_lon, jupiter_lon)
|
||||
results['putra_saham'] = _saham_dict(putra_lon, 'Putra Saham', '子女点——子女、后代、创造力')
|
||||
|
||||
# 9. Jnana Saham(教育点):Jupiter - Mercury + Asc
|
||||
mercury_lon = planet_lons.get('Mercury', 0)
|
||||
jnana_lon = _saham(mercury_lon, jupiter_lon)
|
||||
results['jnana_saham'] = _saham_dict(jnana_lon, 'Jnana Saham', '教育点——学习、知识、智慧')
|
||||
|
||||
# 10. Raja Saham(王权):Sun - Moon + Asc
|
||||
raja_lon = _saham(moon_lon, sun_lon)
|
||||
results['raja_saham'] = _saham_dict(raja_lon, 'Raja Saham', '王权点——领导力、权威')
|
||||
|
||||
# 11. Yasha Saham(名声点):Jupiter - Sun + Asc
|
||||
try:
|
||||
yasha_lon = _calc_formula_saham('Yashas_Saham', planet_lons, asc_lon, is_day, computed_formula_sahams)
|
||||
computed_formula_sahams['Yashas_Saham'] = yasha_lon
|
||||
except KeyError:
|
||||
yasha_lon = _saham(sun_lon, jupiter_lon)
|
||||
results['yasha_saham'] = _saham_dict(yasha_lon, 'Yasha Saham', '名声点——声誉、社会地位')
|
||||
|
||||
# 12. Karma Saham(业力点):Saturn - Mars + Asc
|
||||
try:
|
||||
karma_lon = _calc_formula_saham('Karma_Saham', planet_lons, asc_lon, is_day, computed_formula_sahams)
|
||||
computed_formula_sahams['Karma_Saham'] = karma_lon
|
||||
except KeyError:
|
||||
karma_lon = _saham(mars_lon, saturn_lon)
|
||||
results['karma_saham'] = _saham_dict(karma_lon, 'Karma Saham', '业力点——前世因果、责任')
|
||||
|
||||
# 13. Bandhu Saham(兄弟点):Mars - Mercury + Asc
|
||||
bandhu_lon = _saham(mercury_lon, mars_lon)
|
||||
results['bandhu_saham'] = _saham_dict(bandhu_lon, 'Bandhu Saham', '兄弟点——兄弟姐妹、同辈')
|
||||
|
||||
# 14. Matri Saham(母亲点):Moon - Venus + Asc
|
||||
matri_lon = _saham(venus_lon, moon_lon)
|
||||
results['matri_saham'] = _saham_dict(matri_lon, 'Matri Saham', '母亲点——母亲、养育、情感滋养')
|
||||
|
||||
# 15. Pitri Saham(父亲点):Sun - Saturn + Asc
|
||||
pitri_lon = _saham(saturn_lon, sun_lon)
|
||||
results['pitri_saham'] = _saham_dict(pitri_lon, 'Pitri Saham', '父亲点——父亲、权威、传承')
|
||||
|
||||
# 16. Janma Saham(出生点):出生时刻敏感点
|
||||
j_lon = _saham(sun_lon, asc_lon) # Special: Asc based
|
||||
j_lon = _saham(sun_lon, asc_lon)
|
||||
results['janma_saham'] = _saham_dict(j_lon, 'Janma Saham', '出生点——生命起点、先天禀赋')
|
||||
|
||||
# 17. Mrityu Saham(死亡点):Saturn出生 - 8宫主 + Asc
|
||||
m_lon = _saham(mars_lon, saturn_lon) # 近似
|
||||
results['mrityu_saham'] = _saham_dict(m_lon, 'Mrityu Saham', '死亡点——寿命、终结、转变')
|
||||
|
||||
# 18. Rogha Saham(疾病点):Mercury - Saturn + Asc
|
||||
rogha_lon = _saham(saturn_lon, mercury_lon)
|
||||
results['rogha_saham'] = _saham_dict(rogha_lon, 'Rogha Saham', '疾病点——健康、疾病倾向')
|
||||
|
||||
# 19. Aarogya Saham(健康点):Jupiter - Saturn + Asc
|
||||
aarogya_lon = _saham(saturn_lon, jupiter_lon)
|
||||
results['aarogya_saham'] = _saham_dict(aarogya_lon, 'Aarogya Saham', '健康点——康复、旺盛精力')
|
||||
|
||||
# 20. Bhraatri Saham(同辈点):Mars - Moon + Asc
|
||||
bhratr_lon = _saham(moon_lon, mars_lon)
|
||||
results['bhraatri_saham'] = _saham_dict(bhratr_lon, 'Bhraatri Saham', '同辈点——手足、社交圈')
|
||||
|
||||
# 21. Ghataka Saham(冲突点):Rahu - Mars + Asc
|
||||
rahu_lon = planet_lons.get('Rahu', 0)
|
||||
ghataka_lon = _saham(mars_lon, rahu_lon)
|
||||
results['ghataka_saham'] = _saham_dict(ghataka_lon, 'Ghataka Saham', '冲突点——意外、冲击、突袭')
|
||||
|
||||
# 22. Paradesa Saham(海外点):Saturn - Moon + Asc
|
||||
paradesa_lon = _saham(moon_lon, saturn_lon)
|
||||
results['paradesa_saham'] = _saham_dict(paradesa_lon, 'Paradesa Saham', '海外点——出国、异域、远方')
|
||||
|
||||
# 23. Parabharya Saham(配偶点):Venus - Mars + Asc(女性盘)
|
||||
parab_lon = _saham(mars_lon, venus_lon)
|
||||
results['parabharya_saham'] = _saham_dict(parab_lon, 'Parabharya Saham', '配偶点——伴侣特质')
|
||||
|
||||
# 24. Dhan Saham(财富点):Jupiter - Venus + Asc
|
||||
dhan_lon = _saham(venus_lon, jupiter_lon)
|
||||
results['dhan_saham'] = _saham_dict(dhan_lon, 'Dhan Saham', '财富点——金钱、资产、物质')
|
||||
|
||||
# 25. Maya Saham(幻象点):Rahu - Ketu + Asc
|
||||
ketu_lon = planet_lons.get('Ketu', 0)
|
||||
maya_lon = _saham(ketu_lon, rahu_lon)
|
||||
results['maya_saham'] = _saham_dict(maya_lon, 'Maya Saham', '幻象点——迷惑、欺骗、直觉')
|
||||
|
||||
# 26. Moksha Saham(解脱点):Ketu - Jupiter + Asc
|
||||
moksha_lon = _saham(jupiter_lon, ketu_lon)
|
||||
results['moksha_saham'] = _saham_dict(moksha_lon, 'Moksha Saham', '解脱点——灵性、开悟、超越')
|
||||
|
||||
# 27. Buddha Saham(智慧点):Mercury - Jupiter + Asc
|
||||
buddha_lon = _saham(jupiter_lon, mercury_lon)
|
||||
results['buddha_saham'] = _saham_dict(buddha_lon, 'Buddha Saham', '智慧点——智力、逻辑、语言')
|
||||
|
||||
# 28. Shastra Saham(武器点):Mars - Ketu + Asc
|
||||
shastra_lon = _saham(ketu_lon, mars_lon)
|
||||
results['shastra_saham'] = _saham_dict(shastra_lon, 'Shastra Saham', '武器点——攻击性、防御、技术')
|
||||
|
||||
# 29. Kala Saham(时间点):Saturn - Sun + Asc
|
||||
kala_lon = _saham(sun_lon, saturn_lon)
|
||||
results['kala_saham'] = _saham_dict(kala_lon, 'Kala Saham', '时间点——时机、节奏、耐心')
|
||||
|
||||
# 30. Shakti Saham(力量点):Venus - Moon + Asc
|
||||
shakti_lon = _saham(moon_lon, venus_lon)
|
||||
results['shakti_saham'] = _saham_dict(shakti_lon, 'Shakti Saham', '力量点——女性力量、魅力、创造力')
|
||||
|
||||
# 31. Bhrigu Saham(先知点):Venus - Jupiter + Asc
|
||||
bhrigu_lon = _saham(jupiter_lon, venus_lon)
|
||||
results['bhrigu_saham'] = _saham_dict(bhrigu_lon, 'Bhrigu Saham', '先知点——直觉、预见、智慧传承')
|
||||
|
||||
# 32. Sundara Saham(美点):Venus - Mercury + Asc
|
||||
sundara_lon = _saham(mercury_lon, venus_lon)
|
||||
results['sundara_saham'] = _saham_dict(sundara_lon, 'Sundara Saham', '美点——艺术、美感、和谐')
|
||||
|
||||
# 33. Jnati Saham(亲戚点):Mars - Saturn + Asc
|
||||
jnati_lon = _saham(saturn_lon, mars_lon)
|
||||
results['jnati_saham'] = _saham_dict(jnati_lon, 'Jnati Saham', '亲戚点——宗族、家族关系')
|
||||
|
||||
# 34. Artha Saham(财富点):Sun - Venus + Asc
|
||||
artha_lon = _saham(venus_lon, sun_lon)
|
||||
results['artha_saham'] = _saham_dict(artha_lon, 'Artha Saham', '财富点——物质繁荣、经济')
|
||||
|
||||
# 35. Dharma Saham(正法点):Jupiter - Saturn + Asc
|
||||
dharma_lon = _saham(saturn_lon, jupiter_lon)
|
||||
results['dharma_saham'] = _saham_dict(dharma_lon, 'Dharma Saham', '正法点——正义、道德、人生使命')
|
||||
|
||||
# 36. Sangrama Saham(战斗点):Mars - Rahu + Asc
|
||||
sangrama_lon = _saham(rahu_lon, mars_lon)
|
||||
results['sangrama_saham'] = _saham_dict(sangrama_lon, 'Sangrama Saham', '战斗点——竞争、挑战、胜利')
|
||||
|
||||
formula_meta: Dict[str, Dict] = {}
|
||||
for result_key, rule_name in (
|
||||
('punya_saham', 'Punya_Saham'),
|
||||
('yasha_saham', 'Yashas_Saham'),
|
||||
('karma_saham', 'Karma_Saham'),
|
||||
):
|
||||
try:
|
||||
traced = _formula_saham_trace(rule_name, planet_lons, asc_lon, is_day, computed_formula_sahams)
|
||||
except KeyError:
|
||||
continue
|
||||
formula_meta[result_key] = {
|
||||
'rule_name': traced.get('rule_name'),
|
||||
'formula_operands': traced.get('formula_operands'),
|
||||
'formula_variant': traced.get('formula_variant'),
|
||||
'zodiacal_order_exception_applied': traced.get('zodiacal_order_exception_applied'),
|
||||
}
|
||||
try:
|
||||
from annual_native.saham_traces import attach_formula_traces
|
||||
except ImportError:
|
||||
from scripts.annual_native.saham_traces import attach_formula_traces
|
||||
# Report chapters still read a list of {key, rule, longitude}. Table 74
|
||||
# numbers live on the named rows; this list keeps that export shape.
|
||||
attach_formula_traces(results, is_day=is_day, formula_meta=formula_meta)
|
||||
return results
|
||||
|
||||
|
||||
def _saham_dict(lon: float, name: str, interp: str) -> Dict:
|
||||
def _saham_dict(lon: float, name: str, interp: str, *, trace: Optional[Dict] = None) -> Dict:
|
||||
"""构建Saham字典"""
|
||||
return {
|
||||
result = {
|
||||
'longitude': round(lon, 4),
|
||||
'sign': SIGNS[int(lon / 30) % 12],
|
||||
'sign_cn': SIGNS_CN.get(SIGNS[int(lon / 30) % 12], ''),
|
||||
@@ -1827,6 +1802,21 @@ def _saham_dict(lon: float, name: str, interp: str) -> Dict:
|
||||
'name': name,
|
||||
'interpretation': interp,
|
||||
}
|
||||
if trace:
|
||||
result.update({
|
||||
'formula_status': 'rule_backed_parameter_sensitive',
|
||||
'formula_rule': trace['rule_name'],
|
||||
'formula_variant': trace['formula_variant'],
|
||||
'formula_operands': list(trace['formula_operands']),
|
||||
'zodiacal_order_exception_applied': trace['zodiacal_order_exception_applied'],
|
||||
'base_longitude_before_exception': round(trace['base_longitude'], 4),
|
||||
'formula_source': trace['rule_source'],
|
||||
})
|
||||
if 'house_operand_profile' in trace:
|
||||
result['house_operand_profile'] = trace['house_operand_profile']
|
||||
else:
|
||||
result['formula_status'] = 'legacy_candidate_unverified'
|
||||
return result
|
||||
|
||||
|
||||
# 补充SIGNS_CN(中文星座名)
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import pytest
|
||||
from datetime import datetime
|
||||
from scripts.tajika import _calc_formula_saham, _formula_saham_trace
|
||||
|
||||
|
||||
@pytest.mark.parametrize('is_day,expected', [
|
||||
(True, [280, 295, 160, 100, 310, 280, 295, 250]),
|
||||
(False, [130, 115, 250, 310, 100, 130, 115, 250]),
|
||||
])
|
||||
def test_extended_table_74_rows_survive_full_producer(monkeypatch, is_day, expected):
|
||||
import saham_daynight
|
||||
from scripts.tajika import calc_all_sahams
|
||||
monkeypatch.setattr(saham_daynight, 'determine_daytime',
|
||||
lambda *a, **k: {'is_daytime': is_day})
|
||||
planets = {'Sun': 45., 'Moon': 120., 'Mars': 90., 'Mercury': 60.,
|
||||
'Jupiter': 180., 'Venus': 210., 'Saturn': 300.}
|
||||
result = calc_all_sahams(planets, 10., datetime(2000, 1, 1), lat=0, lon=0, tz=0)
|
||||
keys = ['vivah', 'rajya', 'shatru', 'putra', 'bandhu', 'matri', 'pitri', 'bhraatri']
|
||||
for key, longitude in zip(keys, expected):
|
||||
row = result[key + '_saham']
|
||||
assert row['longitude'] == longitude, key
|
||||
assert row['formula_status'] == 'rule_backed_parameter_sensitive'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('sun,moon,asc,expected,exception', [
|
||||
(45, 120, 10, 115, True), # C outside Sun -> Moon.
|
||||
(45, 120, 60, 135, False),
|
||||
(200, 100, 50, 310, False), # Wrap through zero; C is inside.
|
||||
(200, 100, 150, 80, True),
|
||||
])
|
||||
def test_punya_arc_runs_from_subtracted_to_added_operand(sun, moon, asc, expected, exception):
|
||||
points = {'Sun': sun, 'Moon': moon}
|
||||
assert _calc_formula_saham('Punya_Saham', points, asc, True, {}) == expected
|
||||
trace = _formula_saham_trace('Punya_Saham', points, asc, True, {})
|
||||
assert trace['longitude'] == expected
|
||||
assert trace['zodiacal_order_exception_applied'] is exception
|
||||
|
||||
|
||||
def test_mitra_uses_punya_and_venus_not_venus_and_ascendant():
|
||||
# Table 74: Jupiter - Punya + Venus = 200 - 50 + 100 = 250.
|
||||
trace = _formula_saham_trace('Mitra_Saham', {'Jupiter': 200, 'Venus': 100},
|
||||
300, True, {'Punya_Saham': 50})
|
||||
assert trace['formula_operands'] == ['Jupiter', 'Punya_Saham', 'Venus']
|
||||
assert trace['longitude'] == 250
|
||||
assert not trace['zodiacal_order_exception_applied']
|
||||
|
||||
|
||||
def test_gaurava_keeps_sun_as_third_operand_at_night():
|
||||
trace = _formula_saham_trace('Gaurava_Saham', {'Jupiter': 200, 'Moon': 50, 'Sun': 100},
|
||||
300, False, {})
|
||||
assert trace['formula_operands'] == ['Moon', 'Jupiter', 'Sun']
|
||||
assert trace['longitude'] == 340
|
||||
assert trace['zodiacal_order_exception_applied']
|
||||
|
||||
|
||||
def test_pvr_printed_example_121_artha_arithmetic(monkeypatch):
|
||||
import scripts.tajika as producer
|
||||
monkeypatch.setitem(producer._SAHAM_RULES['sahams'], 'Printed_Artha', {
|
||||
'formula_day': ['SecondHouse', 'Saturn', 'Ascendant'],
|
||||
'formula_night': ['SecondHouse', 'Saturn', 'Ascendant']})
|
||||
actual = producer._calc_formula_saham('Printed_Artha',
|
||||
{'SecondHouse': 310 + 50/60, 'Saturn': 19 + 10/60}, 280 + 50/60, False, {})
|
||||
assert actual == pytest.approx(212.5)
|
||||
|
||||
|
||||
def test_pvr_artha_uses_actual_second_house_and_lord_day_and_night():
|
||||
# Printed example 121: Asc 10Cp50, 2nd house 10Aq50, lord Saturn 19Ar10.
|
||||
for is_day in (True, False):
|
||||
trace = _formula_saham_trace('Artha_Saham', {'Saturn': 19 + 10/60},
|
||||
280 + 50/60, is_day, {})
|
||||
assert trace['longitude'] == pytest.approx(212.5)
|
||||
assert trace['house_operand_profile'] == 'pvr_equal_30_degrees_from_ascendant'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('day,expected', [(True, 260.), (False, 120.)])
|
||||
def test_roga_uses_lagna_and_moon_not_mercury_and_saturn(day, expected):
|
||||
trace = _formula_saham_trace('Roga_Saham', {'Moon': 120}, 10, day, {})
|
||||
assert trace['longitude'] == expected
|
||||
|
||||
|
||||
def test_source_house_formulas_are_not_overwritten_by_legacy_approximations(monkeypatch):
|
||||
import saham_daynight
|
||||
from scripts.tajika import calc_all_sahams
|
||||
monkeypatch.setattr(saham_daynight, 'determine_daytime', lambda *a, **k: {'is_daytime': True})
|
||||
planets = {'Sun':45.,'Moon':120.,'Mars':90.,'Mercury':60.,'Jupiter':180.,'Venus':210.,'Saturn':300.}
|
||||
result = calc_all_sahams(planets, 10., datetime(2000, 1, 1), lat=0, lon=0, tz=0)
|
||||
# House8=220, House9=250 (Jupiter), House2=40 (Venus), House11=310 (Saturn).
|
||||
for name, expected in [('mrityu',140.),('paradesa',110.),('artha',200.),('labha',50.)]:
|
||||
assert result[name+'_saham']['longitude'] == expected
|
||||
assert result[name+'_saham']['formula_status'] == 'rule_backed_parameter_sensitive'
|
||||
@@ -0,0 +1,72 @@
|
||||
"""PVR Table74 pp394–395 and printed Example121 p396; synthetic inputs."""
|
||||
import pytest
|
||||
from scripts.tajika import _formula_saham_trace
|
||||
|
||||
|
||||
def test_printed_example_121_samartha_and_vanik():
|
||||
planets={'Saturn':19+10/60,'Mars':354+58/60,
|
||||
'Moon':345+14/60,'Mercury':311+28/60}
|
||||
asc=280+50/60
|
||||
assert _formula_saham_trace('Samartha_Saham',planets,asc,False,{})['longitude']==pytest.approx(335+2/60)
|
||||
assert _formula_saham_trace('Vanik_Saham',planets,asc,False,{})['longitude']==pytest.approx(247+4/60)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('day,expected',[(True,130),(False,280)])
|
||||
def test_samartha_mars_lagna_exception(day,expected):
|
||||
trace=_formula_saham_trace('Samartha_Saham',{'Mars':90,'Jupiter':180},10,day,{})
|
||||
assert trace['longitude']==expected
|
||||
assert trace['formula_operands']==(['Jupiter','Mars','Ascendant'] if day else ['Mars','Jupiter','Ascendant'])
|
||||
|
||||
|
||||
def test_karyasiddhi_night_changes_luminary_and_its_sign_lord_not_a_b_reversal():
|
||||
planets={'Sun':45,'Moon':120,'Saturn':300,'Venus':210}
|
||||
day=_formula_saham_trace('Karyasiddhi_Saham',planets,10,True,{})
|
||||
night=_formula_saham_trace('Karyasiddhi_Saham',planets,10,False,{})
|
||||
assert day['longitude']==105
|
||||
assert night['longitude']==255
|
||||
assert night['formula_operands']==['Saturn','Moon','MoonSignLord']
|
||||
|
||||
|
||||
def test_all_36_book_rows_reach_full_producer_without_replacing_legacy_names(monkeypatch):
|
||||
from datetime import datetime
|
||||
import saham_daynight
|
||||
from scripts.tajika import calc_all_sahams
|
||||
monkeypatch.setattr(saham_daynight,'determine_daytime',lambda *a,**k:{'is_daytime':True})
|
||||
planets=dict(Sun=45.,Moon=120.,Mars=90.,Mercury=60.,Jupiter=180.,Venus=210.,Saturn=300.,Rahu=0.,Ketu=180.)
|
||||
result=calc_all_sahams(planets,10.,datetime(2000,1,1),lat=0,lon=0,tz=0)
|
||||
assert len(result['formula_traces'])==36
|
||||
assert result['sastra_saham']['formula_status']=='rule_backed_parameter_sensitive'
|
||||
assert result['shastra_saham']['formula_status']=='legacy_candidate_unverified'
|
||||
assert result['sastra_saham']['longitude']!=result['shastra_saham']['longitude']
|
||||
from scripts.pl9_full_data_export import _render_pl9_ai_density_raw_data_markdown_en
|
||||
text=_render_pl9_ai_density_raw_data_markdown_en({
|
||||
'advanced_systems':{'sahams':result}})
|
||||
delivered={c[0]:c for line in text.splitlines() if line.startswith('| ')
|
||||
for c in [[v.strip() for v in line.strip().strip('|').split('|')]]
|
||||
if len(c)==4}
|
||||
for key,row in result.items():
|
||||
if not isinstance(row,dict) or row.get('formula_status')!='rule_backed_parameter_sensitive':continue
|
||||
assert delivered[key][1]==row['sign']
|
||||
assert float(delivered[key][2])==row['degree_in_sign']
|
||||
assert float(delivered[key][3])==row['longitude']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('day,column',[(True,0),(False,1)])
|
||||
def test_remaining_table74_day_night_golden_values(monkeypatch,day,column):
|
||||
# Independent hand arithmetic of Table74 with integer longitudes.
|
||||
# Includes the actual third operand, the Mars-owned-lagna exception,
|
||||
# non-reversing Vyapara and the independently calculated Sastra/Punya.
|
||||
from datetime import datetime
|
||||
import saham_daynight
|
||||
from scripts.tajika import calc_all_sahams
|
||||
monkeypatch.setattr(saham_daynight,'determine_daytime',lambda *a,**k:{'is_daytime':day})
|
||||
planets=dict(Sun=45.,Moon=120.,Mars=90.,Mercury=60.,Jupiter=180.,Venus=210.,Saturn=300.,Rahu=0.,Ketu=180.)
|
||||
result=calc_all_sahams(planets,10.,datetime(2000,1,1),lat=0,lon=0,tz=0)
|
||||
expected={'asha':(250,160),'samartha':(130,280),'jeeva':(160,250),
|
||||
'kali':(130,280),'sastra':(300,210),'paradara':(205,205),
|
||||
'vanik':(100,310),'karyasiddhi':(105,255),'santapa':(340,10),
|
||||
'sraddha':(160,250),'preeti':(225,125),'jadya':(210,300),
|
||||
'vyapara':(160,160),'jalapatana':(175,235),'bandhana':(185,45),
|
||||
'apamrityu':(170,240)}
|
||||
for name,values in expected.items():
|
||||
assert result[name+'_saham']['longitude']==values[column],name
|
||||
Reference in New Issue
Block a user