Chronaxis用户手册English
本页目录

chronaxis.core.time_reduction_numpy

源码基线:8904c2f744951857659abd40aaa3535d57faf53c

1"""用真实数组处理有限块时间极值、规范化时间均值及BOOL TRUE时长。23push把有限CalculationSeries装成(N,)typed列,区间使用(N-1,)int64纳秒差与bool掩码。4min/max保留原dtype;duration用TRUE掩码精确累计纳秒;mean先独立检查整数5浮点精度,第二遍以全窗有效时长规范化权重,再推进本数组原序误差归约。6最终单位仅finish转换一次,不读旧来源、不保存全轴,也不调用参考数值核。7装箱和未知Interval恢复循环只处理协议;数值裁剪、掩码、权重和极值用数组。8状态驱动累计少量标量事实;数组及误差状态预算由外层owner提前预约。9指定step_ns只接收所选驱动真实点;时长/均值按实际交集折算,另有一份有限权重10求和状态。接线者须预约这份状态和当前权重数组,不能沿用actual的费用冒充已计费。11"""1213# 本后端只复用数组原序核与静态模型,数值不委托参考实现。14import numpy as np15from .calculation_output_numpy import convert_result_numpy16from .integral_sum_numpy import NumpyIntegralSum17from .moments import TIME_MOMENTS18from .moments_numpy import NumpyMoments19from .models import Interval20from .numeric_numpy import NumericArray, DTYPES, to_float_numpy21from .types import QualityFlag as Q22from .time_reduction import (TimeCalculationFacts, TimeReductionResult,23                             require_time_config, require_time_series, require_time_options)2425from .time_reduction import MeanTimeBlock26from .mean_weights_numpy import normalized_mean_terms272829class NumpyTimeReduction:30    """保留单候选、精确时长计数及有限误差数组,不保留历史输入列。3132    mean分母为None时只处理第一遍事实,不能finish;第二遍须逐块消费同一历史。33    原序浮点累计属于state_driver,每项误差推进与块掩码均是实际数组运算。34    """3536    def __init__(self, spec, input_unit, dtype, *, selected_duration_ns=None, partial_limit=4096,37                 step_ns=None, selected_weight_seconds=None):38        """检查无条件时间配置并建立本数组后端状态。3940        参数: spec/单位/dtype为原配置;selected_duration_ns为均值全窗有效时长;41            partial_limit为误差容量;step_ns可选周期,周期均值第二遍改用42            selected_weight_seconds。返回: None。异常: 配置、数组分配错误传播。43        """44        self._conversion = require_time_config(spec, input_unit, dtype, selected_duration_ns, partial_limit,45            step_ns=step_ns, selected_weight_seconds=selected_weight_seconds)46        self._unit, self._factor = self._conversion.unit, self._conversion.factor47        self._moments = NumpyMoments(spec.operation) if spec.operation in TIME_MOMENTS else None48        self._spec, self._input_unit, self._dtype = spec, input_unit, dtype49        self._step_ns = step_ns50        self._denominator = selected_duration_ns if step_ns is None else selected_weight_seconds51        # 固定状态不随输入块数增长,输入列生命周期只限当前push。52        self._identity = self._candidate = None53        self._value_duration = self._selected_duration = self._terms = 054        self._numeric_quality = Q.NONE55        self._sum = NumpyIntegralSum(partial_limit) if spec.operation == 'time_weighted_mean' and self._denominator is not None else None56        self._weight_sum = NumpyIntegralSum(partial_limit) if step_ns is not None and spec.operation in ('duration', 'time_weighted_mean') else None5758    @property59    def selected_weight_seconds(self):60        """读取必要的周期累计权重。参数: 无。返回: float或不适用None。异常: 数值故障。"""61        if self._weight_sum is None:62            return None63        result = self._weight_sum.finish('s')64        if result.calculation_failed:65            raise OverflowError('周期权重累计失败')66        return 0.0 if result.value is None else result.value6768    @property69    def selected_duration_ns(self):70        """读取累计选择时长。参数: 无。返回: 原生int纳秒。异常: 无。"""71        return self._selected_duration7273    @property74    def value_valid_duration_ns(self):75        """读取累计有效时长。参数: 无。返回: 原生int纳秒。异常: 无。"""76        return self._value_duration7778    def push(self, series, *, valid_until_ns=None, retain_unknown_ranges=True, retain_mean_terms=False):79        """数组计算有限块事实,并推进对应原单位数值状态。8081        参数: series为无条件、同dtype和原单位CalculationSeries;末点不贡献区间或样本。82        valid_until_ns按实际时间截断;retain_unknown_ranges=False省去未知位置构造。83        retain_mean_terms仅均值首遍返回MeanTimeBlock,其他调用返回事实。84        返回: TimeCalculationFacts;数值权重故障与输入覆盖有效性分别记录。85        异常: 结构、身份、容量或分配错误传播;外层异常后只能清理owner。86        内存: 新数组都是当前(N,)或(N-1,)有限列,不修改输入,也不存入跨块状态。87        """88        actual = require_time_series(series, self._spec, self._input_unit, self._dtype)89        require_time_options(series, self._step_ns, valid_until_ns, retain_unknown_ranges)90        if type(retain_mean_terms) is not bool or retain_mean_terms and (self._spec.operation != 'time_weighted_mean' or self._denominator is not None):91            raise ValueError('仅均值首遍可请求必要回读项')92        if self._identity is not None and self._identity != series.binding_identity:93            raise ValueError('时间归约不能跨绑定身份累计')94        self._identity = series.binding_identity95        # 装箱仅恢复typed列;无效位置0需要显式valid和quality,不能作为正常值。96        column = NumericArray(self._dtype,97                              np.array([value.value if value.valid else 0 for value in series.values], dtype=DTYPES[self._dtype]),98                              np.array([value.valid for value in series.values], dtype=bool),99                              np.array([int(value.quality) for value in series.values], dtype=np.uint16))100        values = to_float_numpy(column) if self._spec.operation == 'time_weighted_mean' or self._moments is not None else column101        times = np.array(series.times_ns, dtype=np.int64)102        start, end = (actual.start_ns, actual.end_ns) if actual is not None else (0, 0)103        # 先裁剪整数端点,仅在正交集做差,避免域外反序减法wrap。104        left = np.maximum(times[:-1], np.int64(start))105        right = np.minimum(times[1:], np.int64(end))106        observed = right > left107        deadlines = np.array([2**63-1 if item is None else item for item in valid_until_ns], dtype=np.int64) if valid_until_ns is not None else None108        value_end = right.copy() if deadlines is None else np.minimum(right, deadlines[:-1])109        value_end = np.maximum(left, np.where(values.valid[:-1], value_end, left))110        durations = np.zeros(len(left), dtype=np.int64)111        usable = observed & (value_end > left)112        np.subtract(value_end, left, out=durations, where=usable)113        selected = usable & values.values[:-1] if self._spec.operation == 'duration' else usable114        # 非负时长总和不超过已校验观测跨度,int64不会wrap;全窗由Python整数累加。115        value_duration = int(np.sum(durations[usable], dtype=np.int64))116        selected_duration = int(np.sum(durations[selected], dtype=np.int64))117        seconds = None118        if self._weight_sum is not None or (self._step_ns is not None and self._moments is not None):119            # 只计算当前有限块;求和后列只读,后续归一化另生成数值结果。120            seconds = durations[selected].astype(np.float64)121            seconds /= np.diff(times)[selected]122            seconds *= self._step_ns / 1_000_000_000123            weight_valid = np.ones(len(seconds), dtype=bool)124            seconds.setflags(write=False)125            weight_valid.setflags(write=False)126            if self._weight_sum is not None:127                self._weight_sum.push_values(seconds, weight_valid)128        terms = int(np.count_nonzero(usable))129        samples = (times >= start) & (times < end) & np.array(series.actual_value_updates, dtype=bool) & values.valid130        if deadlines is not None:131            samples &= times < deadlines132        if self._spec.operation == 'duration':133            samples &= values.values134        # 依赖诊断覆盖正区间与实际更新点;EOF右端不凭点增加时长或样本。135        relevant = np.concatenate((observed, np.array([False]))) | ((times >= start) & (times < end) & np.array(series.actual_value_updates, dtype=bool))136        diagnostics = np.array([int(item) for item in series.diagnostics], dtype=np.uint16) | values.quality137        quality = Q(int(np.bitwise_or.reduce(diagnostics[relevant], initial=np.uint16(0))))138        if np.any(observed & values.valid[:-1] & (value_end < right)):139            quality |= Q.STALE140        unknown = (tuple(Interval(int(value_end[index]), int(right[index])) for index in np.flatnonzero(observed & (value_end < right)))141                   if retain_unknown_ranges else ())142        if self._spec.operation in ('min_time', 'max_time') and terms:143            # 每块极值由原dtype数组归约,跨块仅保存一个精确原生候选。144            candidate = (np.min if self._spec.operation == 'min_time' else np.max)(values.values[:-1][usable])145            if self._candidate is not None:146                previous = DTYPES[self._dtype].type(self._candidate)147                candidate = (np.minimum if self._spec.operation == 'min_time' else np.maximum)(previous, candidate)148            self._candidate = candidate.item()149        elif self._moments is not None:150            measures = durations[usable] if self._step_ns is None else seconds151            self._moments.push(values.values[:-1][usable], measures)152        elif self._spec.operation == 'time_weighted_mean' and self._denominator is not None:153            chosen = values.values[:-1][usable]154            measures = durations[usable].astype(np.float64) if self._step_ns is None else seconds155            weighted, weight_quality = normalized_mean_terms(chosen, measures, self._denominator)156            self._numeric_quality |= weight_quality157            if self._numeric_quality == Q.NONE:158                keep = np.ones(len(weighted), dtype=bool)159                keep.setflags(write=False)160                self._sum.push_values(weighted, keep)161        self._value_duration += value_duration162        self._selected_duration += selected_duration163        self._terms += terms164        facts = TimeCalculationFacts(self._spec, actual, value_duration, selected_duration,165                               int(np.count_nonzero(samples)), unknown, quality, self._input_unit,166                               self._dtype, series.binding_identity, series.source_refs, retain_unknown_ranges)167        if retain_mean_terms:168            selected_values = values.values[:-1][usable]169            selected_weights = durations[usable].copy() if self._step_ns is None else seconds170            selected_values.setflags(write=False)171            selected_weights.setflags(write=False)172            return MeanTimeBlock(facts, selected_values, selected_weights)173        return facts174175    def finish(self):176        """正确舍入并做一次最终转换,生成本后端数值快照。177178        参数: 无。返回: TimeReductionResult,不执行覆盖状态分类。179        异常: RuntimeError为mean第一遍;ValueError为有效时长与全窗分母不符;180            分配或取消传播,数值溢出与非零下溢写成计算失败。181        """182        if self._moments is not None:183            value, quality = self._moments.finish(self._spec.ddof, self._conversion.factor)184            return TimeReductionResult(value, self._unit, quality != Q.NONE, quality, self._terms)185        if self._spec.operation == 'time_weighted_mean':186            if self._denominator is None:187                raise RuntimeError('时间均值第一遍只收事实,不能生成数值')188            measured = self._selected_duration if self._step_ns is None else self.selected_weight_seconds189            if measured != self._denominator:190                raise ValueError('时间均值第二遍有效时长与整窗分母不同')191            if self._numeric_quality != Q.NONE:192                return TimeReductionResult(None, self._unit, True, self._numeric_quality, self._terms)193            # 数组原序核直接执行一次输出转换;映射冻结结果不再次做数值处理。194            number = self._sum.finish(self._input_unit,195                                      1.0)196            if not number.calculation_failed:197                value, quality = convert_result_numpy(number.value, self._dtype, self._conversion)198                return TimeReductionResult(value, self._unit, quality != Q.NONE, quality, self._terms)199            return TimeReductionResult(number.value, self._unit, number.calculation_failed, number.quality, self._terms)200        if self._terms == 0:201            return TimeReductionResult(None, self._unit, False, Q.NONE, 0)202        # BOOL时长先保持精确整数纳秒,再最终换成秒;极值保持原native dtype。203        value = ((float(np.divide(np.float64(self._selected_duration), np.float64(1_000_000_000)))204                  if self._step_ns is None else self.selected_weight_seconds)205                 if self._spec.operation == 'duration' else self._candidate)206        quality = Q.NONE207        # v4换算包含偏移;输入/归约故障不能被正常单位结果覆盖。208        if quality == Q.NONE:209            value, quality = convert_result_numpy(value, self._dtype, self._conversion)210        return TimeReductionResult(None if quality != Q.NONE else value, self._unit,211                                   quality != Q.NONE, quality, self._terms)212
Chronaxis 用户手册 · 离线阅读