chronaxis.core.integral_sum_reference
源码基线:8904c2f744951857659abd40aaa3535d57faf53c
1"""独立按原序累计积分面积,用有界浮点误差项保留大数抵消后的低位。23输入为 ReferenceIntegralTerms,输出为最终单位下的 IntegralSumResult 快照。4每项使用标量精确拆分,保存非重叠误差项;结束时用标准库 fsum 归约误差项。5任何原序中间溢出永久失败;不会靠后续抵消恢复,也不会按块先求和。6状态最多 partial_limit 项,不保存整窗面积;容量不足明确抛错而不截断。7纯核无文件副作用,状态及候选内存由外层 owner 在调用前申请预算。8"""910# 参考实现独立依赖标准库,不调用数组归约、数组面积或数组对齐实现。11from math import fsum, isfinite12from .integral_sum import (IntegralSumResult, IntegralStateLimitError,13 require_partial_limit, require_sum_finish)14from .integral_terms import ReferenceIntegralTerms15from .types import QualityFlag as Q161718class ReferenceIntegralSum:19 """持有有界非重叠误差项、有效面积计数及持久累计故障。2021 push 为原序状态驱动;finish 不清空状态,最终转换故障仅影响返回快照。22 调用方在任意推进异常后须放弃当前累计并清理,不能接着使用半段输入。23 """2425 def __init__(self, partial_limit: int = 4096):26 """建立空累计状态。参数: partial_limit 为硬容量。返回: None。异常: 非法容量。27 """28 # 初始零项没有面积证据;不能提前返回正常零值。29 self._limit = require_partial_limit(partial_limit)30 self._partials = []31 self._valid_terms = 032 self._failed = False3334 @property35 def valid_term_count(self) -> int:36 """读取有效面积项数。参数: 无。返回: int。异常: 无。"""37 return self._valid_terms # 与真实更新样本计数分开。3839 @property40 def calculation_failed(self) -> bool:41 """读取持久累计故障。参数: 无。返回: bool。异常: 无。"""42 return self._failed # 后续抵消不会清除此状态。4344 @property45 def partial_count(self) -> int:46 """读取当前误差项数量。参数: 无。返回: int。异常: 无。"""47 return len(self._partials) # 仅报告有界数值状态,不含输入面积。4849 def push(self, terms: ReferenceIntegralTerms) -> None:50 """按原顺序推进有效面积,保留块间误差状态。5152 参数: terms 为有限冻结面积块。返回: None。53 异常: TypeError 为后端不符;IntegralStateLimitError/MemoryError 为状态容量或分配失败。54 """55 try:56 # 后端模型必须明确,不隐式把 NumPy 数据搬入标量参考核。57 if not isinstance(terms, ReferenceIntegralTerms):58 raise TypeError('参考归约需要ReferenceIntegralTerms')59 # 原积分入口仍检查真实模型;同后端数值推进可用于其他已定义的原序浮点项。60 self.push_values(terms.areas, terms.valid)61 finally:62 # 原异常及TB仍交调用方;只断本层借入参数、视图和临时,不清外部帧。63 self = terms = None6465 def push_values(self, values: tuple[float, ...], valid: tuple[bool, ...]) -> None:66 """累计有限原序浮点项,不要求调用方伪造积分事实。6768 参数: values/valid 为同长冻结tuple;数值为有限原生float,未知位置必须存0。69 返回: None;使用与原积分入口相同的误差状态、容量及中间失败规则。70 异常: TypeError/ValueError 为列结构;容量、分配或取消原样传播。71 边界: 仅复用本参考后端数值算法;调用方负责量纲、覆盖与候选内存预算。72 """73 try:74 # 完整门槛在推进前检查,错误输入不能留下半段新增状态。75 if type(values) is not tuple or type(valid) is not tuple or len(values) != len(valid):76 raise TypeError('原序参考归约需要同长tuple数值与掩码')77 # 显式循环保持原any的逐项/短路顺序;取消时没有genexpr的隐藏zip保留整列。78 for value, keep in zip(values, valid):79 if (type(value) is not float or not isfinite(value) or type(keep) is not bool80 or not keep and value != 0):81 raise ValueError('参考归约需有限float及bool,未知位置存0')82 # 保留原积分算法、原序、中间溢出和有界误差状态,不先归约块和。83 for area, keep in zip(values, valid):84 # 无效存储零不是数值证据;累计失败后仍统计后续有效项。85 if not keep:86 continue87 if self._failed or area == 0:88 self._valid_terms += 189 continue90 # state_driver: 每项在原序状态上拆分,不把块和作为新的输入。91 candidate, current = [], area92 for partial in self._partials:93 large, small = (current, partial) if abs(current) >= abs(partial) else (partial, current)94 high = large + small95 if not isfinite(high):96 # 原序中途溢出保持失败,后续负项不能把已失败计算恢复正常。97 self._failed = True98 candidate = []99 break100 low = small - (high - large)101 if low != 0:102 candidate.append(low)103 current = high104 if self._failed:105 self._partials = []106 self._valid_terms += 1107 continue108 # 零和不占用误差项;容量检查后才安装该项及其计数。109 if current != 0:110 candidate.append(current)111 if len(candidate) > self._limit:112 raise IntegralStateLimitError('积分误差项超过容量,不能截断继续')113 self._partials = candidate114 self._valid_terms += 1115 finally:116 # 原异常及TB仍交调用方;只断本层借入参数、视图和临时,不清外部帧。117 self = values = valid = candidate = None118 current = area = partial = large = small = high = low = None119 value = keep = None120121 def finish(self, unit: str, factor: float = 1.0) -> IntegralSumResult:122 """归约有界误差项并只转换一次,保留原累计状态。123124 参数: unit/factor 为外层核实的最终单位及倍率。返回: 冻结数值快照。125 异常: 非法配置或分配错误传播;数值溢出/转换下溢写入失败结果。126 """127 try:128 # 配置先校验,空证据和持久溢出保持 None,不能回退为零。129 unit, factor = require_sum_finish(unit, factor)130 if self._failed:131 return IntegralSumResult(None, unit, True, Q.NONFINITE, self._valid_terms)132 if self._valid_terms == 0:133 return IntegralSumResult(None, unit, False, Q.NONE, 0)134 try:135 # 仅归约有界误差项;独立标准库求和不调用 NumPy 内核。136 total = fsum(self._partials)137 except OverflowError:138 return IntegralSumResult(None, unit, True, Q.NONFINITE, self._valid_terms)139 value = total * factor140 if not isfinite(value):141 return IntegralSumResult(None, unit, True, Q.NONFINITE, self._valid_terms)142 if total != 0 and value == 0:143 return IntegralSumResult(None, unit, True, Q.PRECISION_LOSS, self._valid_terms)144 # 零值规范为 +0.0;成功的结果不混入输入未知质量。145 return IntegralSumResult(0.0 if value == 0 else value, unit, False, Q.NONE, self._valid_terms)146 finally:147 # 原异常及TB仍交调用方;只断本层借入参数、视图和临时,不清外部帧。148 self = unit = factor = total = value = None149