fix(saham): add 30 degrees when the third operand misses the B-to-A arc
Upstream ce9fed15. The exception now tests C on the forward arc from B to A, in both the value and the trace. BUG-1387. Co-Authored-By: Grok (agent loop) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Grok (agent loop)
Cursor
parent
aa87c0f127
commit
2f8263b6ac
@@ -36593,3 +36593,18 @@
|
||||
- 相关记录:无。
|
||||
- 复发自:无。
|
||||
- 修复版本:分支 `codex/upstream-sync8-20261017`。上游提交 `e7301feb`、`86935c0a`、`4f1536f2`。
|
||||
|
||||
## BUG-1387 | Saham 的 +30° 例外看错了操作数
|
||||
|
||||
- 状态:resolved
|
||||
- 首次发现 / 最近更新:2026-10-17 / 2026-10-18
|
||||
- 来源:issue #80,任务书 T9。上游 `ce9fed15`。检索「add_30_degrees」「Punya_Saham」,无既有编号。
|
||||
- 现象:公式 Saham 在上升不落在第一操作数到第二操作数的顺行弧上时加 30°。规则文件七条公式的第三项都是上升,所以大量结果和 PVR 的判法差 30°。
|
||||
- 触发条件:`_calc_formula_saham`。本仓原先没有 trace 入口。
|
||||
- 根因:例外检查的是上升是否落在 A→B,PVR §28.8.1 检查的是第三操作数 C 是否落在 B→A。
|
||||
- 修复:两个入口都改为 `(C - B) % 360 <= (A - B) % 360`,不满足则结果加 30°。`references/saham_rules.json` 的描述改为同一条。补上 `_formula_saham_trace`,与数值入口使用同一判法。
|
||||
- 验证:`tests/test_saham_directed_operand_contract.py`。`tests/test_tajika.py` 三处断言按三栏改过:Punya 原值 85 / 新值 115;Karma 原值 40 / 新值 70;跨零点例子原值 340 / 新值 310。年运快照测试没有锁这三条公式的旧经度。
|
||||
- 防复发:契约测试覆盖 C 不是上升的公式,以及 trace 与数值入口一致。
|
||||
- 相关记录:无。
|
||||
- 复发自:无。
|
||||
- 修复版本:分支 `codex/upstream-sync8-20261017`。上游提交 `ce9fed15`。
|
||||
|
||||
@@ -46,6 +46,6 @@
|
||||
}
|
||||
},
|
||||
"exception_rules": {
|
||||
"add_30_degrees": "If the Ascendant does not fall between the first and the second point in the zodiacal order, add 30 degrees (1 sign) to the final result."
|
||||
"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."
|
||||
}
|
||||
}
|
||||
|
||||
+30
-8
@@ -96,14 +96,36 @@ def _calc_formula_saham(
|
||||
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
|
||||
# references/saham_rules.json: add one sign when Ascendant is not on the
|
||||
# forward zodiacal arc from the first formula point to the second.
|
||||
ascendant_between_points = (
|
||||
first <= asc_lon <= second
|
||||
if first <= second
|
||||
else asc_lon >= first or asc_lon <= second
|
||||
)
|
||||
return result if ascendant_between_points else (result + 30.0) % 360
|
||||
# PVR §28.8.1, p394: in A-B+C, C must lie on the forward arc B -> A.
|
||||
third_between_points = (third - second) % 360 <= (first - second) % 360
|
||||
return result if third_between_points else (result + 30.0) % 360
|
||||
|
||||
|
||||
def _formula_saham_trace(
|
||||
rule_name: str,
|
||||
planet_lons: Dict[str, float],
|
||||
asc_lon: float,
|
||||
is_day: bool,
|
||||
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']
|
||||
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 {
|
||||
'longitude': (base_longitude + 30.0) % 360 if exception_applied else base_longitude,
|
||||
'base_longitude': base_longitude,
|
||||
'rule_name': rule_name,
|
||||
'formula_operands': formula,
|
||||
'formula_variant': 'day' if is_day else 'night',
|
||||
'zodiacal_order_exception_applied': exception_applied,
|
||||
'rule_source': 'references/saham_rules.json',
|
||||
}
|
||||
|
||||
|
||||
def calc_muntha(birth_asc_idx: int, age: int) -> Dict:
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import pytest
|
||||
from scripts import tajika
|
||||
|
||||
|
||||
@pytest.mark.parametrize('sun,moon,asc,expected,exception', [
|
||||
(45, 120, 10, 115, True),
|
||||
(45, 120, 60, 135, False),
|
||||
(200, 100, 50, 310, False),
|
||||
(200, 100, 150, 80, True),
|
||||
])
|
||||
def test_punya_matches_pvr_directed_arc(sun, moon, asc, expected, exception):
|
||||
points = {'Sun': sun, 'Moon': moon}
|
||||
assert tajika._calc_formula_saham('Punya_Saham', points, asc, True, {}) == expected
|
||||
result = tajika._formula_saham_trace('Punya_Saham', points, asc, True, {})
|
||||
assert result['longitude'] == expected
|
||||
assert result['zodiacal_order_exception_applied'] is exception
|
||||
|
||||
|
||||
def test_third_operand_is_used_even_when_not_ascendant(monkeypatch):
|
||||
monkeypatch.setitem(tajika._SAHAM_RULES['sahams'], 'TestThird', {
|
||||
'formula_day': ['Jupiter', 'Moon', 'Venus'],
|
||||
'formula_night': ['Moon', 'Jupiter', 'Venus'],
|
||||
})
|
||||
points = {'Jupiter': 200., 'Moon': 50., 'Venus': 100.}
|
||||
assert tajika._calc_formula_saham('TestThird', points, 60., True, {}) == 250.
|
||||
assert tajika._calc_formula_saham('TestThird', points, 60., False, {}) == 340.
|
||||
@@ -285,7 +285,8 @@ class TestSahams:
|
||||
def test_punya_saham_formula(self):
|
||||
# Punya = Asc + (Moon - Sun), sourced from references/saham_rules.json
|
||||
sun, moon, asc = 45.0, 120.0, 10.0
|
||||
expected = (asc + (moon - sun)) % 360 # = 10 + 75 = 85
|
||||
# 原值 85 / 新值 115 / 原因:C=Asc10 不在 B=Sun45 → A=Moon120 的顺行弧上,按 PVR p394 加 30°。
|
||||
expected = 115.0
|
||||
planet_lons = {'Sun': sun, 'Moon': moon, 'Mars': 90.0,
|
||||
'Mercury': 60.0, 'Jupiter': 180.0, 'Venus': 210.0,
|
||||
'Saturn': 300.0, 'Rahu': 150.0, 'Ketu': 330.0}
|
||||
@@ -298,14 +299,15 @@ class TestSahams:
|
||||
planet_lons = {'Sun': 45.0, 'Moon': 120.0, 'Mars': 90.0,
|
||||
'Mercury': 60.0, 'Jupiter': 180.0, 'Venus': 210.0,
|
||||
'Saturn': 300.0, 'Rahu': 150.0, 'Ketu': 330.0}
|
||||
expected = (asc + (90.0 - 60.0)) % 360
|
||||
# 原值 40 / 新值 70 / 原因:C=Asc10 不在 B=Mercury60 → A=Mars90 的顺行弧上,加 30°。
|
||||
expected = 70.0
|
||||
result = calc_all_sahams(planet_lons, asc, datetime(1990, 6, 15, 12, 0), lat=39.9042, lon=116.4074, tz=8)
|
||||
assert abs(result['karma_saham']['longitude'] - expected) < 0.01
|
||||
|
||||
def test_formula_saham_applies_reference_add_30_exception(self):
|
||||
from tajika import _calc_formula_saham
|
||||
|
||||
# Asc 50 is outside the forward 100 -> 200 zodiacal arc.
|
||||
# 原值 340 / 新值 310 / 原因:C=Asc50 落在 B=Sun200 → A=Moon100 的跨零点顺行弧上,不加 30°。
|
||||
result = _calc_formula_saham(
|
||||
'Punya_Saham',
|
||||
{'Sun': 200.0, 'Moon': 100.0},
|
||||
@@ -313,7 +315,7 @@ class TestSahams:
|
||||
True,
|
||||
{},
|
||||
)
|
||||
assert result == 340.0
|
||||
assert result == 310.0
|
||||
|
||||
def test_is_faster_moon_vs_sun(self):
|
||||
assert _is_faster('Moon', 'Sun')
|
||||
|
||||
Reference in New Issue
Block a user