Chronaxis用户手册English
本页目录

chronaxis.core.conditional_time_reduction_numpy

源码基线:8904c2f744951857659abd40aaa3535d57faf53c

1"""用真实NumPy掩码归约有限条件时间极值、规范化均值和主BOOL时长。23typed值及外BOOL为(N,)列,正区间为(N-1,)精确int64差与独立bool掩码。极值在原4dtype上归约;duration同时保留外TRUE主FALSE零证据;mean首遍仅确认值精度和5TRUE有效选择,次遍计算x*(dt/D),由本数组原序误差核推进。数值weight故障不改6双覆盖,最终单位只finish转换一次。装箱、恢复未知和固定标量状态驱动不冒充7数组数值核;不调用参考算法、不读来源、不存全轴,有限内存由外层owner预约。8"""910# 原序推进复用独立数组误差状态,只共享结构/typed协议,不调用标量后端。11import numpy as np12from .calculation_output_numpy import convert_result_numpy13from .conditional_time_reduction import ConditionalTimeCalculationFacts, require_conditional_time_series14from .integral_sum_numpy import NumpyIntegralSum15from .moments import TIME_MOMENTS16from .moments_numpy import NumpyMoments17from .calculations import merge_ranges18from .models import Interval19from .numeric_numpy import DTYPES, NumericArray, to_float_numpy20from .time_reduction import TimeReductionResult, require_time_config, require_time_options21from .integral_terms import require_valid_until22from .types import QualityFlag as Q2324from .time_reduction import MeanTimeBlock25from .mean_weights_numpy import normalized_mean_terms262728class NumpyConditionalTimeReduction:29    """保存单原dtype候选、整数时长与有界误差列,不存跨块输入数组。"""3031    def __init__(self, spec, input_unit, dtype, *, selected_duration_ns=None, partial_limit=4096,32                 step_ns=None, selected_weight_seconds=None):33        """校验原配置并建立本数组后端固定状态。3435        参数: spec/input_unit/dtype为原配置;selected_duration_ns为mean次遍全TRUE有效分母;36            partial_limit为误差容量;step_ns选周期,周期均值次遍传selected_weight_seconds。返回: None。异常: 配置、数组分配错误传播。37        """38        # 静态门槛只检查明确配置,两个后端不共享后续数值或覆盖计算。39        self._conversion = require_time_config(spec, input_unit, dtype, selected_duration_ns, partial_limit,40            step_ns=step_ns, selected_weight_seconds=selected_weight_seconds)41        self._unit, self._factor = self._conversion.unit, self._conversion.factor42        self._moments = NumpyMoments(spec.operation) if spec.operation in TIME_MOMENTS else None43        self._spec, self._input_unit, self._dtype = spec, input_unit, dtype44        self._step_ns = step_ns45        self._denominator = selected_duration_ns if step_ns is None else selected_weight_seconds46        # mean首遍不建立误差列,固定候选/计数不随块数增长。47        self._identity = self._candidate = None48        self._value_duration = self._selected_duration = self._terms = 049        self._numeric_quality = Q.NONE50        self._sum = NumpyIntegralSum(partial_limit) if spec.operation == 'time_weighted_mean' and self._denominator is not None else None51        self._weight_sum = NumpyIntegralSum(partial_limit) if step_ns is not None and spec.operation in ('duration', 'time_weighted_mean') else None5253    @property54    def selected_weight_seconds(self):55        """读取必要周期权重。参数: 无。返回: float或不适用None。异常: 权重求和故障。"""56        if self._weight_sum is None:57            return None58        result = self._weight_sum.finish('s')59        if result.calculation_failed:60            raise OverflowError('条件周期权重累计失败')61        return 0.0 if result.value is None else result.value6263    @property64    def selected_duration_ns(self):65        """读取累计已选时长。参数: 无。返回: 原生int纳秒。异常: 无。"""66        return self._selected_duration  # duration仅主TRUE;mean为规范化分母事实。6768    @property69    def value_valid_duration_ns(self):70        """读取累计值有效时长。参数: 无。返回: 原生int纳秒。异常: 无。"""71        return self._value_duration  # 独立于外条件FALSE/UNKNOWN。7273    def push(self, series, *, valid_until_ns=None, condition_valid_until_ns=None, retain_unknown_ranges=True, retain_mean_terms=False):74        """计算有限块双覆盖并按原序推进本后端数值候选。7576        参数: series为同dtype/原单位且显式BOOL条件的有限CalculationSeries。77        valid_until_ns/condition_valid_until_ns分别截断实际有效期;78            retain_unknown_ranges=False省去两类未知范围。79        retain_mean_terms仅均值首遍返回MeanTimeBlock,其他调用返回事实。80        返回: ConditionalTimeCalculationFacts;权重故障仅改变数值失败状态,不改输入覆盖。81        异常: 结构、身份、容量、分配或取消错误传播,外层异常后只能清理owner。82        内存: 所有新输入/掩码列形状N或N-1,仅当前push存活,不修改调用者输入。83        """84        # 当前输入先核对显式条件/单位/dtype和交集,跨块原值绑定不能变化。85        actual = require_conditional_time_series(series, self._spec, self._input_unit, self._dtype)86        require_time_options(series, self._step_ns, valid_until_ns, retain_unknown_ranges)87        require_valid_until(series.times_ns, condition_valid_until_ns)88        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):89            raise ValueError('仅均值首遍可请求必要回读项')90        if self._identity is not None and self._identity != series.binding_identity:91            raise ValueError('条件时间归约不能跨绑定身份累计')92        self._identity = series.binding_identity93        # typed装箱仅恢复协议,值精度转换只对mean执行,极值保持int64/uint64原数组。94        column = NumericArray(self._dtype,95            np.array([value.value if value.valid else 0 for value in series.values], dtype=DTYPES[self._dtype]),96            np.array([value.valid for value in series.values], dtype=bool),97            np.array([int(value.quality) for value in series.values], dtype=np.uint16))98        values = to_float_numpy(column) if self._spec.operation == 'time_weighted_mean' or self._moments is not None else column99        known = np.array([condition.valid for condition in series.conditions], dtype=bool)100        truth = np.array([condition.value if condition.valid else False for condition in series.conditions], dtype=bool)101        condition_quality = np.array([int(condition.quality) for condition in series.conditions], dtype=np.uint16)102        times = np.array(series.times_ns, dtype=np.int64)103        start, end = (actual.start_ns, actual.end_ns) if actual is not None else (0, 0)104        # 整数端点先裁剪,只对正区间做差,域外反序不发生int64减法wrap。105        left, right = np.maximum(times[:-1], np.int64(start)), 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        condition_deadlines = np.array([2**63-1 if item is None else item for item in condition_valid_until_ns], dtype=np.int64) if condition_valid_until_ns is not None else None109        value_end = right.copy() if deadlines is None else np.minimum(right, deadlines[:-1])110        condition_end = right.copy() if condition_deadlines is None else np.minimum(right, condition_deadlines[:-1])111        value_end = np.maximum(left, np.where(values.valid[:-1], value_end, left))112        condition_end = np.maximum(left, np.where(known[:-1], condition_end, left))113        # 三种交集独立做安全整数差,原始记录对仍留作周期权重分母。114        value_dt = np.zeros(len(left), dtype=np.int64)115        condition_dt = np.zeros(len(left), dtype=np.int64)116        durations = np.zeros(len(left), dtype=np.int64)117        np.subtract(value_end, left, out=value_dt, where=observed)118        np.subtract(condition_end, left, out=condition_dt, where=observed)119        np.subtract(np.minimum(value_end, condition_end), left, out=durations, where=observed)120        usable = observed & (durations > 0) & truth[:-1]121        selected = usable & values.values[:-1] if self._spec.operation == 'duration' else usable122        selected_false_mask = usable & ~values.values[:-1] if self._spec.operation == 'duration' else np.zeros(len(left), dtype=bool)123        # 所有非负dt归约不超过已检查int64实际跨度,跨块由Python整数累加。124        value_duration = int(np.sum(value_dt, dtype=np.int64))125        known_duration = int(np.sum(condition_dt, dtype=np.int64))126        selected_duration = int(np.sum(durations, where=selected, dtype=np.int64))127        selected_unknown = int(np.sum(condition_dt - durations, where=truth[:-1], dtype=np.int64))128        false_duration = int(np.sum(condition_dt, where=~truth[:-1], dtype=np.int64))129        selected_false = int(np.sum(durations, where=selected_false_mask, dtype=np.int64))130        terms = int(np.count_nonzero(usable))131        seconds = None132        if self._weight_sum is not None or (self._step_ns is not None and self._moments is not None):133            seconds = durations[selected].astype(np.float64)134            seconds /= np.diff(times)[selected]135            seconds *= self._step_ns / 1_000_000_000136            keep_weights = np.ones(len(seconds), dtype=bool)137            seconds.setflags(write=False)138            keep_weights.setflags(write=False)139            if self._weight_sum is not None:140                self._weight_sum.push_values(seconds, keep_weights)141        # 半开原值真实更新且外TRUE才成为样本;duration另要求主TRUE,条件更新不计。142        actual_samples = (times >= start) & (times < end) & np.array(series.actual_value_updates, dtype=bool)143        samples = actual_samples & values.valid & known & truth144        if deadlines is not None:145            samples &= times < deadlines146        if condition_deadlines is not None:147            samples &= times < condition_deadlines148        if self._spec.operation == 'duration':149            samples &= values.values150        relevant = actual_samples.copy()151        relevant[:-1] |= observed152        diagnostics = np.array([int(flag) for flag in series.diagnostics], dtype=np.uint16) | values.quality | condition_quality153        quality = Q(int(np.bitwise_or.reduce(diagnostics, where=relevant, initial=0)))154        # 元数据循环只恢复两种未知Interval,覆盖和数值运算均已由真实数组完成。155        if np.any(observed & ((values.valid[:-1] & (value_end < right)) | (known[:-1] & (condition_end < right)))):156            quality |= Q.STALE157        value_unknown = (tuple(Interval(int(value_end[index]), int(right[index])) for index in np.flatnonzero(observed & (value_end < right)))158                         if retain_unknown_ranges else ())159        condition_unknown = (tuple(Interval(int(condition_end[index]), int(right[index])) for index in np.flatnonzero(observed & (condition_end < right)))160                             if retain_unknown_ranges else ())161        if self._spec.operation in ('min_time', 'max_time') and terms:162            # 当前块原dtype极值,跨块仅比较一个同dtype候选,避免uint64精度舍入。163            candidate = (np.min if self._spec.operation == 'min_time' else np.max)(values.values[:-1][usable])164            if self._candidate is not None:165                previous = DTYPES[self._dtype].type(self._candidate)166                candidate = (np.minimum if self._spec.operation == 'min_time' else np.maximum)(previous, candidate)167            self._candidate = candidate.item()168        elif self._moments is not None:169            measures = durations[usable] if self._step_ns is None else seconds170            self._moments.push(values.values[:-1][usable], measures)171        elif self._spec.operation == 'time_weighted_mean' and self._denominator is not None:172            chosen = values.values[:-1][usable]173            measures = durations[usable].astype(np.float64) if self._step_ns is None else seconds174            weighted, weight_quality = normalized_mean_terms(chosen, measures, self._denominator)175            self._numeric_quality |= weight_quality176            if self._numeric_quality == Q.NONE:177                keep = np.ones(len(weighted), dtype=bool)178                keep.setflags(write=False)179                self._sum.push_values(weighted, keep)180        self._value_duration += value_duration181        self._selected_duration += selected_duration182        self._terms += terms183        facts = ConditionalTimeCalculationFacts(self._spec, actual, value_duration, known_duration, selected_duration,184            selected_unknown, false_duration, selected_false, int(np.count_nonzero(samples)), merge_ranges(value_unknown),185            merge_ranges(condition_unknown), quality, self._input_unit, self._dtype, series.binding_identity, series.source_refs, retain_unknown_ranges)186        if retain_mean_terms:187            selected_values = values.values[:-1][usable]188            selected_weights = durations[usable].copy() if self._step_ns is None else seconds189            selected_values.setflags(write=False)190            selected_weights.setflags(write=False)191            return MeanTimeBlock(facts, selected_values, selected_weights)192        return facts193194    def finish(self):195        """生成原序统计快照并做一次最终换单位,双覆盖由外层分类。196197        参数: 无。返回: 既有TimeReductionResult。异常: mean首遍RuntimeError、次遍分母不符ValueError;198            配置/分配/取消传播,数值溢出和非零下溢返回计算失败。199        """200        # mean首遍禁交付数值;次遍需要实际TRUE有效累计时长与全窗D相等。201        if self._moments is not None:202            value, quality = self._moments.finish(self._spec.ddof, self._conversion.factor)203            return TimeReductionResult(value, self._unit, quality != Q.NONE, quality, self._terms)204        if self._spec.operation == 'time_weighted_mean':205            if self._denominator is None:206                raise RuntimeError('条件时间均值首遍只收事实,不能生成数值')207            measured = self._selected_duration if self._step_ns is None else self.selected_weight_seconds208            if measured != self._denominator:209                raise ValueError('条件均值次遍选择时长与全窗分母不同')210            if self._numeric_quality != Q.NONE:211                return TimeReductionResult(None, self._unit, True, self._numeric_quality, self._terms)212            number = self._sum.finish(self._input_unit,213                                      1.0)214            if not number.calculation_failed:215                value, quality = convert_result_numpy(number.value, self._dtype, self._conversion)216                return TimeReductionResult(value, self._unit, quality != Q.NONE, quality, self._terms)217            return TimeReductionResult(number.value, self._unit, number.calculation_failed, number.quality, self._terms)218        # 没有正区间数值证据不补0;全外FALSE空0由外层完整请求分类证明。219        if self._terms == 0:220            return TimeReductionResult(None, self._unit, False, Q.NONE, 0)221        # 主FALSE为有效duration项而时长0;极值未换单位时保持原生精确整数。222        value = ((float(np.divide(np.float64(self._selected_duration), np.float64(1_000_000_000)))223                  if self._step_ns is None else self.selected_weight_seconds)224                 if self._spec.operation == 'duration' else self._candidate)225        quality = Q.NONE226        # v4换算包含偏移;输入/归约故障不能被正常单位结果覆盖。227        if quality == Q.NONE:228            value, quality = convert_result_numpy(value, self._dtype, self._conversion)229        return TimeReductionResult(None if quality != Q.NONE else value, self._unit, quality != Q.NONE, quality, self._terms)230
Chronaxis 用户手册 · 离线阅读