fix(engine): dispositor cycles are not collapsed to one planet; dasa convergence drops uncalibrated probabilities (upstream e9beae6b)
final_dispositor_from_chain and its regression test are upstream yinduzhanxing e9beae6b; probability is None with probability_basis. The Narayana convergence additions of that commit are out of scope. TASK-upstream-sync4-20261002 T7. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
08376cf0a8
commit
bc9b9f89bb
+28
-21
@@ -472,6 +472,29 @@ def calc_inter_chart_linkage(planet_name, d1_data, d9_data, d10_data, d12_data=N
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def final_dispositor_from_chain(chain):
|
||||||
|
"""Return a terminal only for a self-loop; retain multi-body cycles.
|
||||||
|
|
||||||
|
Ported from upstream yinduzhanxing e9beae6b (TASK-upstream-sync4-20261002 T7).
|
||||||
|
"""
|
||||||
|
path = []
|
||||||
|
for row in chain:
|
||||||
|
planet, target = row.get('planet'), row.get('dispositor')
|
||||||
|
if not planet or not target or (path and path[-1] != planet):
|
||||||
|
return None
|
||||||
|
if not path:
|
||||||
|
path.append(planet)
|
||||||
|
if target == planet:
|
||||||
|
return planet
|
||||||
|
if target in path:
|
||||||
|
cycle = path[path.index(target):]
|
||||||
|
rotations = [cycle[i:] + cycle[:i] for i in range(len(cycle))]
|
||||||
|
cycle = min(rotations)
|
||||||
|
return 'Cycle: ' + ' -> '.join(cycle + cycle[:1])
|
||||||
|
path.append(target)
|
||||||
|
return None # incomplete or depth-limited chain is not a terminal
|
||||||
|
|
||||||
|
|
||||||
def calc_all_dispositor_chains(planets_data):
|
def calc_all_dispositor_chains(planets_data):
|
||||||
"""为所有行星计算定位星链"""
|
"""为所有行星计算定位星链"""
|
||||||
result = {}
|
result = {}
|
||||||
@@ -15023,21 +15046,20 @@ def _calc_dasa_convergence(dasha_result, chara_dasha_result, yogini_result, plan
|
|||||||
sc = info['system_count']
|
sc = info['system_count']
|
||||||
if sc >= 4:
|
if sc >= 4:
|
||||||
info['convergence_level'] = 'L5'
|
info['convergence_level'] = 'L5'
|
||||||
info['probability'] = '85-92%'
|
|
||||||
info['interpretation'] = '四个及以上推运系统同时激活,顶级收敛信号'
|
info['interpretation'] = '四个及以上推运系统同时激活,顶级收敛信号'
|
||||||
elif sc >= 3:
|
elif sc >= 3:
|
||||||
info['convergence_level'] = 'L4'
|
info['convergence_level'] = 'L4'
|
||||||
info['probability'] = '75-85%'
|
|
||||||
info['interpretation'] = '三系统同时激活,极强信号'
|
info['interpretation'] = '三系统同时激活,极强信号'
|
||||||
elif sc >= 2:
|
elif sc >= 2:
|
||||||
info['convergence_level'] = 'L3'
|
info['convergence_level'] = 'L3'
|
||||||
info['probability'] = '50-65%'
|
|
||||||
info['interpretation'] = '双系统激活,强信号'
|
info['interpretation'] = '双系统激活,强信号'
|
||||||
else:
|
else:
|
||||||
info['convergence_level'] = 'L1'
|
info['convergence_level'] = 'L1'
|
||||||
info['probability'] = '+15-20%'
|
|
||||||
info['interpretation'] = '单系统激活,需Transit确认'
|
info['interpretation'] = '单系统激活,需Transit确认'
|
||||||
|
|
||||||
|
info['probability'] = None
|
||||||
|
info['probability_basis'] = 'No empirical probability calibration; level counts activating systems only.'
|
||||||
|
|
||||||
# 收敛窗口(最高优先级的领域)
|
# 收敛窗口(最高优先级的领域)
|
||||||
top_domains = sorted(domain_activations.items(), key=lambda x: x[1]['system_count'], reverse=True)[:5]
|
top_domains = sorted(domain_activations.items(), key=lambda x: x[1]['system_count'], reverse=True)[:5]
|
||||||
|
|
||||||
@@ -15725,23 +15747,8 @@ def cmd_full_reading(args):
|
|||||||
report['modules']['inter_chart_linkage'] = ic_result
|
report['modules']['inter_chart_linkage'] = ic_result
|
||||||
|
|
||||||
# 补充:每个行星的"最终定位星"(Dispositor Chain 的最后一个非循环元素)
|
# 补充:每个行星的"最终定位星"(Dispositor Chain 的最后一个非循环元素)
|
||||||
final_dispositors = {}
|
# A multi-planet cycle has no unique terminal dispositor.
|
||||||
for pname, chain in dc_result.items():
|
final_dispositors = {p: final_dispositor_from_chain(c) for p, c in dc_result.items()}
|
||||||
if chain:
|
|
||||||
# 找到最后一个不重复的元素
|
|
||||||
seen = set()
|
|
||||||
last_valid = None
|
|
||||||
for item in chain:
|
|
||||||
if item['dispositor'] in seen:
|
|
||||||
break
|
|
||||||
seen.add(item['dispositor'])
|
|
||||||
last_valid = item
|
|
||||||
if last_valid:
|
|
||||||
final_dispositors[pname] = last_valid['dispositor']
|
|
||||||
else:
|
|
||||||
final_dispositors[pname] = chain[-1]['dispositor'] if chain else None
|
|
||||||
else:
|
|
||||||
final_dispositors[pname] = None
|
|
||||||
report['modules']['final_dispositors'] = final_dispositors
|
report['modules']['final_dispositors'] = final_dispositors
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
"""Do not collapse a directed dispositor cycle into an arbitrary terminal."""
|
||||||
|
import ast
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def resolver():
|
||||||
|
# Isolate the pure function without importing the large CLI and adapters.
|
||||||
|
source = Path(__file__).parents[1] / 'scripts/jyotish_engine.py'
|
||||||
|
tree = ast.parse(source.read_text())
|
||||||
|
node = next(n for n in tree.body if isinstance(n, ast.FunctionDef)
|
||||||
|
and n.name == 'final_dispositor_from_chain')
|
||||||
|
namespace = {}
|
||||||
|
exec(compile(ast.Module(body=[node], type_ignores=[]), str(source), 'exec'), namespace)
|
||||||
|
return namespace[node.name]
|
||||||
|
|
||||||
|
|
||||||
|
def chain(*names):
|
||||||
|
return [{'planet':a,'dispositor':b} for a,b in zip(names,names[1:])]
|
||||||
|
|
||||||
|
|
||||||
|
def test_cycle_rotation_and_leading_planet():
|
||||||
|
f = resolver()
|
||||||
|
expected = 'Cycle: Jupiter -> Mercury -> Jupiter'
|
||||||
|
assert f(chain('Mercury','Jupiter','Mercury')) == expected
|
||||||
|
assert f(chain('Jupiter','Mercury','Jupiter')) == expected
|
||||||
|
assert f(chain('Venus','Jupiter','Mercury','Jupiter')) == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_terminal_and_incomplete_are_distinct():
|
||||||
|
f = resolver()
|
||||||
|
assert f(chain('Sun','Mars','Saturn','Saturn')) == 'Saturn'
|
||||||
|
assert f(chain('Sun','Mars')) is None
|
||||||
|
assert f([]) is None
|
||||||
|
assert f(chain('Sun','Mars') + chain('Venus','Venus')) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_three_body_cycle_preserves_direction():
|
||||||
|
assert resolver()(chain('Sun','Mars','Moon','Sun')) == 'Cycle: Mars -> Moon -> Sun -> Mars'
|
||||||
Reference in New Issue
Block a user