Chronaxis用户手册English
本页目录

chronaxis.core.calculations_reference

源码基线:8904c2f744951857659abd40aaa3535d57faf53c

1"""独立计算保持值积分、时间统计、TRUE时长和真实样本统计。23compute_reference接收CalculationSeries和CalculationDefinition,先裁剪观测与请求窗口,4再独立计算面积、样本归约、覆盖时长及未知区间,输出CalculationResult。5积分使用零阶保持:左端值乘以相邻区间裁剪后的时长。先对整数纳秒端点6求差,再换算秒,不把较大的绝对时间先转成浮点数。7时间极值包含裁剪边界的保持值,time_weighted_mean按有效选择时长加权;duration8累计有效主BOOL为TRUE的区间。select_reference保留真实TRUE/UNKNOWN更新行。910样本统计只取actual_value_updates为True的真实数值更新,不包含窗口或11有效期到期边界,也不包括半开窗口末点。值有效与条件已知分别统计,12条件FALSE是已知未选中,UNKNOWN(未知)不能视作FALSE或补成零。1314本实现只使用标准库与核心模型,数值和覆盖不调用NumPy。可共享输入15检查、单位与结果分类,但归约独立实现。保留完整内存输入,不读写文件,16不修改原数据或持有跨任务状态;本模块不提供流式内存预算保证。17"""1819# 独立标量有限性/稳定求和,不调用数组归约。20from math import fsum, isfinite21# 共享模型、单位与条件始终为FALSE时的空选择规则,不共享数值计算。22from .calculations import CalculationSeries, CalculationDefinition, make_calculation_result, calculation_conversion23# 诊断区间与请求都保持精确纳秒。24from .models import Interval25# 独立参考定宽到float转换先检查精度。26from .numeric import ScalarValue, to_float_reference27# 未知/计算质量与正常零严格分开。28from .types import QualityFlag as Q, SignalDType as D, Truth29# 选择只共享冻结协议,标量筛选独立于NumPy掩码。30from .selection import SelectionRow, require_selection, partial_selection31from .record_intervals import clipped_record_seconds, require_record_step32from .calculation_output_reference import convert_result_reference33from .affine import affine_reference34from .moments import MOMENTS, TIME_MOMENTS, compute_moment_series35from .quantiles import SAMPLE_QUANTILES, compute_quantile_series363738def require_calculation(series, spec, *, step_ns=None):39    """验证实际指标输入模型,不执行任何面积或统计。4041    参数: series真实CalculationSeries,spec真实CalculationDefinition;step_ns可指定记录对周期,42        仅连续统计算子可用,输入必须是所选驱动真实更新。43    返回: None,模型内部完整轴和dtype已校验。44    异常: TypeError为无类型占位输入;ValueError为select标量执行或dtype不匹配。45    """46    # 后端共享typed协议,不复用被测数值。47    if not isinstance(series, CalculationSeries) or not isinstance(spec, CalculationDefinition):48        # 不猜dict内不完整状态。49        raise TypeError('指标执行需CalculationSeries和CalculationDefinition')50    # 行流具有独立返回类型,不能作为标量统计执行。51    if spec.operation == 'select':52        raise ValueError('select需选择行流入口')53    # 布尔只用于TRUE时长;数值统计不接受布尔或枚举编码。54    allowed = (D.BOOL,) if spec.operation == 'duration' else (D.INT64, D.UINT64, D.FLOAT64)55    if series.values[0].dtype not in allowed:56        raise ValueError('指标算子与输入dtype不匹配')57    if step_ns is not None and spec.operation not in ('integral', 'duration', 'time_weighted_mean', 'min_time', 'max_time', *TIME_MOMENTS):58        raise ValueError('样本统计不接受记录对周期')59    require_record_step(step_ns, series.actual_value_updates)606162def select_reference(series, spec):63    """按真实更新逐行返回TRUE与UNKNOWN选择结果,不累计输出列表。6465    参数: series完整内存CalculationSeries;spec为select请求。66    返回: SelectionRow迭代器;UNKNOWN保留诊断,FALSE排除,窗口右端不输出。67    异常: 开始迭代时传播模型/算子/单位错误。68    流程: 遍历完整时间轴,只处理请求与观测交集内的真实目标依赖更新。69        合成边界与条件独自更新不产生行;无效值保持原质量,不填正常零。70    """71    # 生成器检查发生在消费时;只共享输入门槛。72    require_selection(series, spec)73    conversion = calculation_conversion(spec, series.unit)74    start = max(spec.window.start_ns, series.observation.start_ns)75    end = min(spec.window.end_ns, series.observation.end_ns)76    # 输入已经完整物化,输出每次只创建当前行。77    for index, time in enumerate(series.times_ns):78        # 不插入保持值行、不读取EOF之后的数据。79        if not start <= time < end or not series.actual_value_updates[index]:80            continue81        # 未提供条件就是明确TRUE,未知条件单独诊断。82        condition = series.conditions[index] if series.conditions is not None else ScalarValue(True, D.BOOL)83        selection = Truth.UNKNOWN if not condition.valid else Truth.TRUE if condition.value else Truth.FALSE84        if selection is Truth.FALSE:85            continue86        # 原值保持dtype/质量/标签与绑定,UNKNOWN不是自动选择成功。87        value = series.values[index]88        # 当前输出只保留换算后的一份值;原样本及实际时间不改写。89        if conversion is not None and (conversion.factor != 1 or conversion.offset != 0):90            value = affine_reference(value, conversion.factor, conversion.offset, conversion.dtype(value.dtype))91        quality=series.diagnostics[index] | value.quality | condition.quality92        if series.values[index].partial or condition.partial:93            value,quality=partial_selection(value,quality,spec)94        yield SelectionRow(time, value, selection, quality,95                           series.binding_identity, series.source_refs, series.unit if conversion is None else conversion.unit)969798def compute_reference(series, spec, *, step_ns=None, budget=None):99    """独立计算请求窗口内的积分、样本/时间统计或BOOL TRUE时长。100101    参数:102        series: 完整CalculationSeries;数值统计需同一数值dtype,duration需BOOL。103        spec: CalculationDefinition,给出左闭右开纳秒窗口、算子、输出单位和部分结果政策。104        step_ns: 可选固定/DBC周期,仅用于所选驱动的真实记录对;按交集比例105            计算面积、时长和均值权重,不改变实际坐标或输出第二套时间。106        budget为精确分位数的可选共享RAM账本,省略时采用项目默认配额。107    返回:108        CalculationResult。未换单位的整数极值可保持精确整数;积分和样本平均109        返回浮点值;时间平均先规范化时长权重,duration输出秒。无法核实时110        为None,并保留覆盖、来源与质量原因;select使用独立行流入口。111    异常:112        输入模型或单位配置错误直接传播TypeError或ValueError。113        非有限结果、下溢或转换丢精度作为质量原因返回,不吞配置错误。114    流程:115        裁剪窗口后逐区间计算保持面积与三值选择交集,再按真实更新归约样本,116        最后应用一次输出单位转换并分类结果。正常零与未知占位分别处理。117        临时值和诊断列表只用于本次计算,不修改调用者的CalculationSeries。118    """119    # 配置错误不能吞成未知数据。120    require_calculation(series, spec, step_ns=step_ns)121    if spec.operation in SAMPLE_QUANTILES:122        from .sample_reduction_reference import ReferenceSampleReduction123        return compute_quantile_series(series, spec, ReferenceSampleReduction, budget)124    if spec.operation in MOMENTS:125        from .sample_reduction_reference import ReferenceSampleReduction126        from .time_reduction_reference import ReferenceTimeReduction127        from .conditional_time_reduction_reference import ReferenceConditionalTimeReduction128        return compute_moment_series(series, spec, ReferenceSampleReduction, ReferenceTimeReduction,129                                     ReferenceConditionalTimeReduction, step_ns)130    # 仅显式已知维度转换,保留有符号面积。131    conversion = calculation_conversion(spec, series.unit)132    unit, factor = conversion.unit, conversion.factor133    # 先求真实观测与请求交集,不外推末点/EOF。134    start = max(spec.window.start_ns, series.observation.start_ns)135    # 半开窗口右端实际观测不足必须裁剪。136    end = min(spec.window.end_ns, series.observation.end_ns)137    # 完全无交集与零域分别由政策解释。138    actual = Interval(start, end) if start <= end else None139    # 整数输入进入float面积/mean前先严格验证,不影响精确min/max。140    values = [to_float_reference(value) if spec.operation in ('integral', 'sample_mean', 'time_weighted_mean') else value for value in series.values]141    # 全部覆盖时长使用Python精确整数,不按样本数量估算。142    value_duration, known_duration, selected_duration, selected_unknown = 0, 0, 0, 0143    # 明确FALSE时长用来证明充分观测空选择,不靠sum空数组。144    false_duration = 0145    # 真实未知区间按原因类型分别记录,不改变原输入。146    value_unknown, condition_unknown, areas = [], [], []147    # 时间统计保留每个正裁剪区间的左端保持值和整数纳秒时长。148    timed_values = []149    # duration必须至少有可核实的主布尔值与TRUE选择区间,不能靠外部UNKNOWN补零。150    duration_evidence = 0151    # 原诊断与计算原因完整OR,不将KNOWN逻辑结果改成UNKNOWN。152    quality = Q.NONE153    # 独立参考逐区间求裁剪/面积/三值交集。154    for index in range(len(series.times_ns)-1):155        # 先裁剪精确整数端点,不先转float绝对坐标。156        left, right = max(series.times_ns[index], start), min(series.times_ns[index+1], end)157        # 窗口外或零区间不产生面积/未知时长。158        if left >= right:159            # 边界point不是额外周期。160            continue161        # 先整数差再换算秒,保留大绝对时间的小间隔。162        duration = right - left163        # 当前ZOH值与当前选择独立有效性。164        value = values[index]165        # 无条件表示明确选择全部,而不是未知选择。166        condition = series.conditions[index] if series.conditions is not None else ScalarValue(True, D.BOOL)167        # 原依赖、数值与条件质量均可追溯。168        quality |= series.diagnostics[index] | value.quality | condition.quality169        # 正常积分面积先计算,非有限/下溢不变成正常0。170        area = None171        # 只计算真实选择的有效值,FALSE不制造所选值缺失。172        if spec.operation == 'integral' and value.valid and condition.valid and condition.value:173            # 相对dt转秒,默认ZOH保持当前左端值。174            seconds = (duration / 1_000_000_000 if step_ns is None else175                       clipped_record_seconds(series.times_ns[index], series.times_ns[index + 1],176                                              spec.window, step_ns=step_ns))177            area = value.value * seconds178            # 有限输入乘积溢出和非零乘积下溢显式失效。179            failure = Q.NONFINITE if not isfinite(area) else Q.PRECISION_LOSS if area == 0 and value.value != 0 else Q.NONE180            # 不参与不可信面积的正常归约。181            if failure != Q.NONE:182                # 元数据值仍保留,只有计算输出置未知。183                value = ScalarValue(value.value, D.FLOAT64, False, failure)184                # 相同真实样本的积分可用性也更新。185                values[index] = value186                # 新原因不覆盖原诊断。187                quality |= failure188        # 值有效时长与条件真假独立统计。189        if value.valid:190            # 有效零值计入覆盖,区别于UNKNOWN。191            value_duration += duration192        else:193            # 不跨窗口外范围保存未知。194            value_unknown.append(Interval(left, right))195        # 条件UNKNOWN独立保存;FALSE属于已知条件覆盖。196        if condition.valid:197            # 真假都可判断,不把FALSE当缺失。198            known_duration += duration199            # 只有明确FALSE累计空选择证据。200            if not condition.value:201                # 不凭无选中结果推测FALSE。202                false_duration += duration203            # 真与有效值交集才算选中时长。204            elif value.valid:205                # FALSE主值仍证明已知零时长;外部条件未知不提供该证据。206                duration_evidence += duration207                # 所选面积和时长分别保留。208                selected_duration += duration if spec.operation != 'duration' or value.value else 0209                # 其他样本算子没有连续面积。210                if spec.operation == 'integral':211                    # 正常零面积合法进入归约。212                    areas.append(area)213                # min/max_time包含裁剪左边界保持值,time_weighted_mean使用同一有效选择集合。214                if spec.operation in ('min_time', 'max_time', 'time_weighted_mean'):215                    weight = (clipped_record_seconds(series.times_ns[index], series.times_ns[index + 1],216                                                     spec.window, step_ns=step_ns)217                              if spec.operation == 'time_weighted_mean' and step_ns is not None else duration)218                    timed_values.append((value.value, weight))219                elif spec.operation == 'duration' and value.value and step_ns is not None:220                    areas.append(clipped_record_seconds(series.times_ns[index], series.times_ns[index + 1],221                                                        spec.window, step_ns=step_ns))222            else:223                # 所选值未知时strict/report有必要覆盖不足。224                selected_unknown += duration225        else:226            # 条件未知不能当FALSE产生空选择零。227            condition_unknown.append(Interval(left, right))228    # 样本口径只计真实原依赖更新,不计TTL/裁剪合成边界。229    selected_samples = []230    sample_count = 0231    # 参考逐原完整时间组统计当前值/条件,不跨域复制末点。232    for index, time in enumerate(series.times_ns):233        # 半开实际窗口内真实更新,末点排除。234        if not start <= time < end or not series.actual_value_updates[index]:235            # 不把窗口左端保持值伪装成真实样本。236            continue237        # 未知样本保留在诊断,只有有效/TRUE进入样本统计。238        value = values[index]239        # 无选择条件全部真实有效样本可用。240        condition = series.conditions[index] if series.conditions is not None else ScalarValue(True, D.BOOL)241        # 完整实际样本的质量也纳入诊断。242        quality |= series.diagnostics[index] | value.quality | condition.quality243        # 不将无效原数据存储占位0当正常样本。244        if value.valid and condition.valid and condition.value and (spec.operation != 'duration' or value.value):245            # 仅恢复当前原生逻辑数值,未改原输入。246            sample_count += 1247            if spec.operation != 'sample_count':248                selected_samples.append(value.value)249    # 空归约保持None;仅明确all_false时按空选择规则处理。250    result, failed = None, False251    # 已选可核实面积或样本才计算正常值。252    try:253        # 面积与样本统计使用不同明确口径。254        if spec.operation == 'integral' and areas:255            # fsum减少标量累计舍入,但不替代NumPy归约。256            result = fsum(areas)257        # 精确整数极值无需浮点转换。258        elif spec.operation == 'sample_count':259            result = sample_count260        elif selected_samples and spec.operation == 'sample_minimum':261            # 窗口裁剪不增加边界合成样本。262            result = min(selected_samples)263        # 同样保留uint64大整数精度。264        elif selected_samples and spec.operation == 'sample_maximum':265            # min/max不按时间加权。266            result = max(selected_samples)267        # mean明确样本算术平均,不叫模糊mean。268        elif selected_samples and spec.operation == 'sample_mean':269            # 有效样本数量作分母,不能用时长替代。270            result = fsum(selected_samples) / len(selected_samples)271        # 时间极值只计正时长保持区间,保留大整数的原生精度。272        elif timed_values and spec.operation == 'min_time':273            result = min(value for value, _ in timed_values)274        elif timed_values and spec.operation == 'max_time':275            result = max(value for value, _ in timed_values)276        # 先规范化权重,避免平均值有限但值乘纳秒的中间面积溢出。277        elif timed_values and spec.operation == 'time_weighted_mean':278            denominator = selected_duration if step_ns is None else fsum(weight for _, weight in timed_values)279            terms = [value * (duration / denominator) for value, duration in timed_values]280            if any(not isfinite(term) for term in terms):281                failed, quality = True, quality | Q.NONFINITE282            elif any(term == 0 and value != 0 for term, (value, _) in zip(terms, timed_values)):283                failed, quality = True, quality | Q.PRECISION_LOSS284            else:285                result = fsum(terms)286        # 已知FALSE可核实为零;全未知没有候选值,不能返回正常零。287        elif spec.operation == 'duration' and duration_evidence > 0:288            result = selected_duration / 1_000_000_000 if step_ns is None else fsum(areas)289    # 只把明确浮点累计溢出转计算质量,不吞接口错误。290    except OverflowError:291        # 无法核实总值不报正常Inf。292        failed, quality = True, quality | Q.NONFINITE293    # 归约后应用一次完整仿射转换,并检查目标数值精度。294    if result is not None and not failed and spec.operation != 'sample_count':295        result, conversion_quality = convert_result_reference(result, series.values[0].dtype, conversion)296        failed = conversion_quality != Q.NONE297        quality |= conversion_quality298    # 完整全FALSE证明需要非零实际域,最后政策还检查请求充分覆盖。299    all_false = series.conditions is not None and actual is not None and end > start and false_duration == end-start300    # 共同政策仅包装各独立内核已计算的事实。301    return make_calculation_result(spec, series, result, unit, actual, value_duration, known_duration, selected_duration,302                              selected_unknown, sample_count, value_unknown, condition_unknown, quality,303                              all_false=all_false, calculation_failed=failed)304
Chronaxis 用户手册 · 离线阅读