chronaxis.core.where_updates
源码基线:8904c2f744951857659abd40aaa3535d57faf53c
1"""按 where 当前选择判断某一组是否更新输出,独立于输出的数值是否改变。23输入是原子提交后的输入、同一时刻排除该组的旧输入,以及各别名的真实更新标志。4只在条件实际更新时比较前后选择;因此仅刷新有效条件不会采样,失效后恢复会采样。5普通算子取各参数更新并集,嵌套选择递归套用同一协议;不保存时间历史。6"""789def contains_where(root):10 """检查已编译 DAG 是否需要分支更新口径。参数: 根节点。返回: bool。异常: 非法节点。"""11 pending, seen = [root], set()12 while pending:13 node = pending.pop()14 if node.operator == 'where':15 return True16 if id(node) not in seen:17 seen.add(id(node))18 pending.extend(node.args)19 return False202122def reference_updates(root, current, before, updates):23 """按原子组计算标量更新标志。2425 参数: 根节点、当前/组前标量映射和真实更新标志;内部传 before=None 仅判断活动性。26 返回: 是否存在活动更新。异常: 输入/节点与绑定不符时沿用标量表达式核错误。27 """28 from .expression_reference import _evaluate2930 now_cache, old_cache, flags = {}, {}, {}3132 def visit(node):33 """判断当前活动路径。参数: 子树节点。返回: bool。异常: 同外层求值。"""34 if node not in flags:35 if node.operator == 'name':36 flag = updates[node.name]37 elif node.operator == 'where':38 condition, when_true, when_false = node.args39 now = _evaluate(condition, current, now_cache).value40 changed = visit(condition)41 if before is None:42 flag = changed or (now.valid and visit(when_true if now.value else when_false))43 elif not now.valid:44 flag = changed45 else:46 # 当前分支即使数值相同也采样;条件刷新则须有切换或恢复。47 flag = visit(when_true if now.value else when_false)48 if changed and not flag:49 old = _evaluate(condition, before, old_cache).value50 flag = not old.valid or old.value != now.value51 else:52 flag = any(visit(arg) for arg in node.args)53 flags[node] = flag54 return flags[node]5556 return bool(visit(root))575859def numpy_updates(root, current, before, updates, size):60 """以 NumPy 活动子批检查整列更新证据,不逐点解释。6162 参数: 根节点、当前/组前列映射、别名的更新列、行数;内部 before=None 时仅判断活动性。63 返回: 一维布尔列。异常: 原数组布局/求值错误传播。64 """65 import numpy as np66 from .expression_numpy import _evaluate67 from .numeric_numpy import NumericArray6869 def condition_at(node, inputs, indices):70 """按活动行解释。参数: 条件子树、输入列及行号。返回: BOOL 列。异常: 数组求值错误。"""71 columns = {}72 for name in node.aliases:73 col = inputs[name]74 columns[name] = col.take(indices)75 return _evaluate(node, columns, len(indices), {}).value7677 def visit(node, indices, cache):78 """同一活动行集内复用证据。参数: 子树、行号、局部缓存。返回: 布尔列。异常: 数组错误。"""79 if node in cache:80 return cache[node]81 flag = np.zeros(len(indices), dtype=bool)82 if node.operator == 'name':83 flag = updates[node.name][indices]84 elif node.operator == 'where' and len(indices):85 condition, when_true, when_false = node.args86 now = condition_at(condition, current, indices)87 changed = visit(condition, indices, cache)88 flag |= changed if before is None else changed & ~now.valid89 for selected, branch in ((now.values, when_true), (~now.values, when_false)):90 rows = now.valid & selected91 if np.any(rows):92 flag[rows] |= visit(branch, indices[rows], {})93 # 仅比较有效条件刷新且未被活动分支更新证明的剩余行。94 rows = changed & now.valid & ~flag95 if before is not None and np.any(rows):96 old = condition_at(condition, before, indices[rows])97 flag[rows] |= ~old.valid | (old.values != now.values[rows])98 else:99 for arg in node.args:100 flag |= visit(arg, indices, cache)101 cache[node] = flag102 return flag103104 return visit(root, np.arange(size), {})105106107def reference_active(root, current, flags):108 """只检查活动输入。参数: 根节点、当前标量、别名标志。返回: bool。异常: 标量求值错误。"""109 return reference_updates(root, current, None, flags)110111112def numpy_active(root, current, flags, size):113 """按活动子批检查输入。参数: 根节点、当前列、标志列、长度。返回: 布尔列。异常: 数组错误。"""114 return numpy_updates(root, current, None, flags, size)115