chronaxis.timebase.numpy_ops
源码基线:8904c2f744951857659abd40aaa3535d57faf53c
1"""对一列纳秒时间执行固定偏移或计算相邻时间差,返回独立的新数组。23输入必须是shape=(N,)、dtype=int64的ndarray,不接受浮点、uint64、object或4MaskedArray。先通过比较和布尔掩码证明加减安全,再执行NumPy数组运算,5保留同时间的零差值,允许空输入。输入只读使用,不排序或去重。6本模块只共享时间常量和标量偏移校验,不调用Python参考数值内核。输出和7临时掩码占用O(N)空间;没有文件操作、CAN访问或全局状态。这里只实现时间8运算,不能据此认定全部分析均已有数组实现。9"""1011# NumPy仅在显式选择本模块时导入;核心时间入口本身不依赖它。12import numpy as np1314# 共享已经校验的整数协议;不导入参考加减函数。15from .conversion import INT64_MAX, INT64_MIN, TimeOverflowError, require_int64_ns161718def _require_time_array(times: np.ndarray) -> None:19 """检查数组形状和定宽dtype,不以转换/广播修补错误输入。2021 参数:22 times (ndarray): shape=(N,)、dtype=int64的纳秒列。23 返回:24 None: 只读验证,不转换或复制输入。25 异常:26 TypeError: 非ndarray、MaskedArray或dtype不为原生int64。27 ValueError: 不是一维列数组。28 """29 # 检查真实数组,禁止把Python循环包装为隐式批量转换。30 if not isinstance(times, np.ndarray) or np.ma.isMaskedArray(times) or times.dtype != np.dtype("int64"):31 # 时间坐标不能缺失;拒绝整个MaskedArray协议,不能偷偷丢掩码或强制转换数据类型。32 raise TypeError("时间数组必须为无MaskedArray掩码、dtype=int64的ndarray")33 # 核心是单列时间轴,不允许维度广播掩盖调用错误。34 if times.ndim != 1:35 # 同样拒绝标量、矩阵或多维输入。36 raise ValueError("时间数组形状必须为(N,)")373839def shift_times_numpy(times: np.ndarray, offset_ns: int) -> np.ndarray:40 """给全部时间坐标添加固定纳秒偏移,运算前先检查每项结果的范围。4142 参数:43 times (ndarray[int64], shape=(N,)): 纳秒坐标,允许负值和未排序输入。44 offset_ns (Integral): int64范围内的固定偏移,单位为纳秒。45 返回:46 ndarray[int64], shape=(N,): 独立的新数组;输入不修改,空输入返回空数组。47 异常:48 TypeError或ValueError: 输入类型、数据类型或形状不符合要求。49 TimeOverflowError: 偏移本身或任一偏移后坐标超出int64范围。50 副作用:51 只分配输出和比较掩码,不调用参考相加函数或读取文件。52 """53 # 验证数组形状和数据类型,不先做可能有损的转换。54 _require_time_array(times)55 # 标量协议可以共享,数值批量算法独立实现。56 offset = require_int64_ns(offset_ns, "offset_ns")57 # 正偏移的上界MAX-offset在Python整数域计算,没有中间wrap。58 if offset > 0 and np.any(times > INT64_MAX - offset):59 # 任一位置失败则整次时间映射拒绝,不填0丢弃数据。60 raise TimeOverflowError("固定偏移导致时间上溢")61 # 负偏移用MIN-offset为安全下界,包含MIN等于合法。62 if offset < 0 and np.any(times < INT64_MIN - offset):63 # 该检查在ufunc之前,保证不执行下溢。64 raise TimeOverflowError("固定偏移导致时间下溢")65 # 所有位置安全后执行一次int64数组运算,返回O(N)独立新数组。66 return np.add(times, np.int64(offset))676869def time_deltas_numpy(times: np.ndarray) -> np.ndarray:70 """计算非递减时间轴的相邻纳秒差,先检查顺序及跨度再做数组减法。7172 参数:73 times (ndarray[int64], shape=(N,)): 非递减纳秒轴,允许相同时间。74 返回:75 ndarray[int64], shape=(max(N-1, 0),): 独立的新数组,保存非负相邻跨度。76 异常:77 TypeError或ValueError: 输入类型、数据类型或形状错误,或者时间严格回退。78 TimeOverflowError: 两端坐标均合法,但相邻跨度超过int64上限。79 处理流程:80 对负左端计算MAX+left作为安全右端阈值,右端严格超出阈值时拒绝。81 全部检查通过后再减法,不先计算可能回绕的差值。82 副作用:83 输入只读使用;输出、阈值和掩码需要与输入长度成比例的空间。84 """85 # 验证真实时间列,禁止浮点绝对时间参与差值。86 _require_time_array(times)87 # 两个shape=(N-1,)只读视图,无永久共同矩阵或输入复制。88 left, right = times[:-1], times[1:]89 # 比较原整数即可判断回退,不会在差值中间发生溢出。90 if np.any(right < left):91 # 相等合法,来源内乱序不排序或忽略。92 raise ValueError("时间序列必须非递减")93 # 只有负left会产生跨符号大跨度风险,掩码shape=(N-1,)。94 negative_left = left < 095 # 阈值数组独立分配;非负left位置初始化0但不会参与溢出判定。96 safe_right_limit = np.zeros_like(left)97 # 负left+MAX总在int64范围内;where防止非负位置执行溢出加法。98 np.add(left, np.int64(INT64_MAX), out=safe_right_limit, where=negative_left)99 # 等于阈值的MAX跨度合法,严格超过才拒绝。100 if np.any(negative_left & (right > safe_right_limit)):101 # 在subtract前失败,不保留wrap值或转float回避问题。102 raise TimeOverflowError("相邻时间跨度超出int64范围")103 # 经过所有检查后,合法结果由真实数组减法一次计算。104 return np.subtract(right, left)105