chronaxis.core.condition_evidence
源码基线:8904c2f744951857659abd40aaa3535d57faf53c
1"""把同轴的更新前点、连续后态及当前点求值结果组装成条件证据。23输入为调用方已经求出的三个BOOL视图、整数纳秒轴,以及实际更新、真实无效更新、4来源结束和会话terminal四种独立标志;输出冻结ConditionTick tuple。更新前点只用于5证明真实更新是否导致UNKNOWN,不把其反事实诊断加入当前真实诊断。terminal强制6连续状态UNKNOWN并叠加MISSING,当前点仍可保留真正末点的TRUE或FALSE。7参考实现逐点使用标量结果,数组实现独立执行valid/quality与因果掩码运算,最后装箱。8本模块不求值表达式、不读取来源、不访问state或时钟、不生成时间轴,也不更新检测器。9数组临时列及输出规模随N增长,后续调用方必须先申请预算;本纯内核不是有限执行器。10NumPy只在数组入口加载,参考入口及模块导入保持标准库核心的依赖边界。11"""1213# 已求值结果和冻结证据共享协议;这里不调用任一表达式或保持数值核。14from .expression_reference import Evaluation15from .numeric import ScalarValue16from .temporal import ConditionTick17from .types import QualityFlag as Q, SignalDType as D, Truth18# 参考轴先要求原生int,再沿用已有int64端点检查,不进行t-1或差分运算。19from ..timebase.conversion import require_int64_ns202122def _reference_truth(result):23 """把已经验证的BOOL标量转为三值,未知存储占位不参加真假判断。2425 参数: result当前Evaluation,调用入口已检查ScalarValue及BOOL声明。26 返回: Truth.TRUE、Truth.FALSE或Truth.UNKNOWN。27 异常: 无主动异常;调用者必须先完成typed结果校验。28 """29 # valid先于值解释,无效None/False等占位都只表示UNKNOWN。30 if not result.value.valid:31 return Truth.UNKNOWN32 # 只有已验证的原生BOOL正常值进入二值分支,不对任意数值使用truthiness。33 return Truth.TRUE if result.value.value else Truth.FALSE343536def condition_ticks_reference(times, before, continuous, point, actual_updates,37 invalid_updates, observation_ends, terminals):38 """独立逐点组装完整条件证据,不从来源推断更新标志或补造时间。3940 参数:41 times: 原生int构成的tuple,单位ns,int64范围内严格递增。42 before/continuous/point: 同长Evaluation tuple,三个视图的结果dtype均为BOOL。43 actual_updates: 同长原生bool tuple,当前条件依赖中是否有真实更新。44 invalid_updates: 同长原生bool tuple,实际选中的更新样本是否无效;45 不把保持值因TTL到期而无效算作真实无效更新,True必须同时有actual。46 observation_ends: 同长原生bool tuple,条件依赖的来源是否在此刻结束观测。47 terminals: 同长原生bool tuple,会话连续域是否结束;至多唯一末项,48 不要求同时存在来源EOF,普通批次全False与空输入均合法。49 返回:50 tuple[ConditionTick, ...],保留连续/点质量、真实因果及当前依赖诊断。51 异常:52 TypeError为容器、原生元素、模型或非BOOL;ValueError为长度、回退/重复时间、53 非末terminal或invalid缺actual;OverflowError为时间越界;MemoryError为分配失败。54 副作用:55 只创建当前有限输入的输出模型,不修改任何输入;申请预算由调用者负责。56 """57 # 明确固定tuple协议,不能从任意迭代器隐式收集全量来源。58 columns = (before, continuous, point, actual_updates, invalid_updates, observation_ends, terminals)59 if type(times) is not tuple or any(type(column) is not tuple for column in columns):60 raise TypeError('参考条件证据需要时间、三视图及四标志tuple')61 # 行数必须已经对齐,禁止zip静默截断或广播修补错位证据。62 size = len(times)63 if any(len(column) != size for column in columns):64 raise ValueError('条件证据各输入长度必须相同')65 # 时间不接受bool/NumPy标量;直接比较避免极值差分或t-1溢出。66 previous = None67 for time in times:68 if type(time) is not int:69 raise TypeError('参考条件证据时间需要原生int纳秒')70 require_int64_ns(time)71 if previous is not None and time <= previous:72 raise ValueError('条件证据时间必须严格递增')73 previous = time74 # 四种标志不能用整数或字符串真假代替真实证据。75 for column in (actual_updates, invalid_updates, observation_ends, terminals):76 if any(type(value) is not bool for value in column):77 raise TypeError('参考条件证据标志需要原生bool')78 # 任一前置terminal都会违反唯一末组契约;普通批次允许完全没有terminal。79 if any(terminals[index] for index in range(max(0, size - 1))):80 raise ValueError('terminal只能唯一出现在输入末项')81 if any(invalid and not actual for invalid, actual in zip(invalid_updates, actual_updates)):82 raise ValueError('真实无效更新必须同时有actual_update')83 # Evaluation自身不验证字段,先核对真实标量/诊断协议与BOOL声明再解释值。84 for column in (before, continuous, point):85 for result in column:86 if (not isinstance(result, Evaluation) or not isinstance(result.value, ScalarValue)87 or not isinstance(result.dependency_quality, Q)):88 raise TypeError('参考三视图需要typed Evaluation/ScalarValue/QualityFlag')89 if result.value.dtype is not D.BOOL:90 raise TypeError('条件证据三视图必须为BOOL,数值不能隐式转bool')91 # 参考逐点只使用当前三个结果;不调用数组实现或重新查询历史。92 ticks = []93 for index, time in enumerate(times):94 old, current, observed = before[index], continuous[index], point[index]95 old_state = _reference_truth(old)96 state, point_state = _reference_truth(current), _reference_truth(observed)97 # known更新前点、实际无效或新增结果质量位证明未知来自真实更新。98 causal = (invalid_updates[index] or old_state is not Truth.UNKNOWN99 or bool(observed.value.quality & ~old.value.quality))100 actual_unknown = actual_updates[index] and point_state is Truth.UNKNOWN and causal101 # 半开连续域在真实terminal结束,但不更改闭区间当前点的证据。102 quality = current.value.quality103 if terminals[index]:104 state, quality = Truth.UNKNOWN, quality | Q.MISSING105 # 仅当前连续与当前点的依赖原因属于真实诊断,before反事实原因排除。106 diagnostic = current.dependency_quality | observed.dependency_quality107 ticks.append(ConditionTick(time, state, quality, actual_updates[index],108 observation_ends[index], terminals[index], point_state,109 observed.value.quality, actual_unknown, diagnostic))110 # 冻结输出供检测器按完整组推进;空输入自然得到空tuple。111 return tuple(ticks)112113114def condition_ticks_numpy(times, before, continuous, point, actual_updates,115 invalid_updates, observation_ends, terminals):116 """独立按列构造因果与三值证据,最后仅恢复不可变ConditionTick模型。117118 参数:119 times: 精确ndarray,一维int64纳秒轴,严格递增。120 before/continuous/point: 同轴ArrayEvaluation,NumericArray逻辑dtype为BOOL。121 actual_updates: 精确ndarray,形状(N,)的bool真实更新列。122 invalid_updates: 同形状bool列,实际选中更新样本无效;TTL不属于真实更新,123 True必须有同时刻actual_updates=True。124 observation_ends: 同形状bool列,条件依赖的来源观测结束标志。125 terminals: 同形状bool列,会话连续域结束标志,至多唯一末项;126 不要求来源EOF同时成立,空轴及无terminal合法。127 返回:128 tuple[ConditionTick, ...];数值/因果计算整列完成,装箱不调用参考实现。129 异常:130 TypeError为数组布局、模型或非BOOL;ValueError为shape、非递增时间或矛盾标志;131 MemoryError为列/输出分配失败。不隐式cast、不调用vectorize或标量参考数值核。132 内存与副作用:133 临时状态/quality/因果/诊断列均为(N,),不修改借用的输入;预算须由调用者先申请。134 """135 # 延迟加载数组依赖,使只导入/使用参考入口时不引入NumPy。136 import numpy as np137 from .expression_numpy import ArrayEvaluation138 from .numeric_numpy import NumericArray139 # 轴的物理布局明确,不用asarray/astype隐式转换任意输入。140 if type(times) is not np.ndarray or times.ndim != 1 or times.dtype != np.dtype('int64'):141 raise TypeError('数组条件证据时间需要一维精确int64 ndarray')142 # 比较相邻坐标而非np.diff,INT64_MIN到INT64_MAX的合法递增不会回绕。143 if np.any(times[1:] <= times[:-1]):144 raise ValueError('条件证据时间必须严格递增')145 # 四标志是同形状bool列;禁止广播或对象数组真值进入因果运算。146 for column in (actual_updates, invalid_updates, observation_ends, terminals):147 if type(column) is not np.ndarray or column.ndim != 1 or column.dtype != np.dtype('bool'):148 raise TypeError('数组条件证据标志需要一维精确bool ndarray')149 if column.shape != times.shape:150 raise ValueError('条件证据标志与时间shape必须相同')151 # 终点只能是当前输入末项,真实无效标志必须有同时刻实际更新。152 if np.any(terminals[:-1]):153 raise ValueError('terminal只能唯一出现在输入末项')154 if np.any(invalid_updates & ~actual_updates):155 raise ValueError('真实无效更新必须同时有actual_update')156 # 三个静态视图复用已校验模型,仅检查本次BOOL和同轴布局;不复制或重新求值。157 for result in (before, continuous, point):158 if not isinstance(result, ArrayEvaluation) or not isinstance(result.value, NumericArray):159 raise TypeError('数组三视图需要typed ArrayEvaluation/NumericArray')160 if result.value.dtype is not D.BOOL:161 raise TypeError('条件证据三视图必须为BOOL,数值不能隐式转bool')162 if result.value.values.shape != times.shape or result.dependency_quality.shape != times.shape:163 raise ValueError('条件证据三视图与时间shape必须相同')164 # 因果来自实际invalid、更新前known或点结果新增质量位,整列掩码独立于参考。165 causal = (invalid_updates | before.value.valid166 | ((point.value.quality & ~before.value.quality) != 0))167 actual_unknown = actual_updates & ~point.value.valid & causal168 # 形状(N,)的整数状态列使用0/1/2对应TRUE/FALSE/UNKNOWN;无效False占位不决定真值。169 continuous_states = np.where(continuous.value.valid, np.where(continuous.value.values, 0, 1), 2)170 point_states = np.where(point.value.valid, np.where(point.value.values, 0, 1), 2)171 # 只修改新建连续列,terminal不延长面积;借用的点结果与质量保持原样。172 continuous_states[terminals] = 2173 quality = continuous.value.quality.copy()174 quality[terminals] |= np.uint16(Q.MISSING)175 # 当前实际诊断整列合并,刻意排除更新前反事实诊断。176 diagnostic = continuous.dependency_quality | point.dependency_quality177 # 列运算已完成,只在末端把形状(N,)存储恢复为原生int/bool及枚举模型。178 truths = (Truth.TRUE, Truth.FALSE, Truth.UNKNOWN)179 return tuple(ConditionTick(int(time), truths[int(continuous_states[index])], Q(int(quality[index])),180 bool(actual_updates[index]), bool(observation_ends[index]), bool(terminals[index]),181 truths[int(point_states[index])], Q(int(point.value.quality[index])),182 bool(actual_unknown[index]), Q(int(diagnostic[index]))) for index, time in enumerate(times))183