Chronaxis用户手册English
本页目录

chronaxis.core.time_reduction

源码基线:8904c2f744951857659abd40aaa3535d57faf53c

1"""声明有限时间统计的事实和数值结果,不执行时间或数值归约。23TimeCalculationFacts保留一个块的有效时长、TRUE时长、真实更新计数和完整来源。4TimeReductionResult只保存一次最终换单位后的候选数值及计算故障。5无条件时间极值、时间均值和BOOL时长使用各自独立参考/NumPy核;本模块6仅共享类型、单位和输入结构门槛,不读文件、不保留全轴、不管理预算。7纯核及返回的有限事实由外层owner提前预约内存,留存结果也须由外层计费。8"""910# 冻结协议与有限输出检查只依赖通用核心,不依赖NumPy。11from .moments import TIME_MOMENTS12from dataclasses import dataclass13from math import isfinite14from .calculations import CalculationDefinition, CalculationSeries, calculation_conversion15from .models import Interval, SourceRef16from .types import SignalDType as D, QualityFlag as Q17from .validation import require_text18from .integral_sum import require_partial_limit19from .record_intervals import require_record_step20from .integral_terms import require_valid_until212223@dataclass(frozen=True)24class TimeCalculationFacts:25    """保存有限块的真实覆盖事实;FALSE仍是有效BOOL,不能算成未知。2627    selected_duration_ns在数值算子等于有效时长,在duration只计有效TRUE。28    sample_count只计半开块内真实更新,duration另要求TRUE,不计共享右端。29    time_weighted_mean的值有效性已包含整数到float的精度检查;权重计算故障不改覆盖。30    """3132    spec: CalculationDefinition  # 原请求、政策和scope,不按当前块重写。33    actual_window: Interval | None  # 当前块与原请求的真实交集。34    value_valid_duration_ns: int  # 所有有效值的整数纳秒时长。35    selected_duration_ns: int  # 数值有效时长,或BOOL TRUE时长。36    valid_sample_count: int  # 已选真实更新计数,不是保持区间数。37    value_unknown_ranges: tuple[Interval, ...]  # 完整未知范围,按时序保存。38    quality: Q  # 输入、精度及依赖原因,不表示必须全数值失败。39    input_unit: str  # 原分析单位,尚未做最终输出转换。40    dtype: D  # 原声明类型,极值保留精确整数。41    binding_identity: str  # 实际表达式绑定身份。42    source_refs: tuple[SourceRef, ...]  # 完整定位,保留真实子类和字段。43    ranges_retained: bool = True  # 精简执行可不构造未知位置,不能用于完整覆盖证据。4445    def __post_init__(self):46        """核对覆盖划分及真实字段类型,不修复错误事实。4748        参数: 无。返回: None。异常: TypeError/ValueError为结构或矛盾时长。49        """50        # 原请求和dtype必须符合时间统计的支持范围。51        require_time_config(self.spec, self.input_unit, self.dtype, None, 4096)52        if self.actual_window is not None and not isinstance(self.actual_window, Interval):53            raise TypeError('时间事实实际范围需要Interval或None')54        span = 0 if self.actual_window is None else self.actual_window.end_ns - self.actual_window.start_ns55        if self.actual_window is not None and (self.actual_window.start_ns < self.spec.window.start_ns56                                               or self.actual_window.end_ns > self.spec.window.end_ns):57            raise ValueError('时间事实范围超出原请求')58        # 纳秒与计数保持原生整数,布尔不能作为时长进入账本。59        if (any(type(value) is not int for value in (self.value_valid_duration_ns,60                self.selected_duration_ns, self.valid_sample_count))61                or not 0 <= self.selected_duration_ns <= self.value_valid_duration_ns <= span62                or not 0 <= self.valid_sample_count <= self.selected_duration_ns):63            raise ValueError('时间事实有效时长、选择或样本计数矛盾')64        if self.spec.operation != 'duration' and self.selected_duration_ns != self.value_valid_duration_ns:65            raise ValueError('无条件数值的选择时长必须等于有效时长')66        # 有效与未知必须完整划分正区间,不接受重复、越界或无序诊断。67        if type(self.value_unknown_ranges) is not tuple:68            raise TypeError('时间事实未知范围需要tuple')69        cursor = self.actual_window.start_ns if self.actual_window is not None else 070        unknown = 071        for item in self.value_unknown_ranges:72            if (not isinstance(item, Interval) or item.start_ns < cursor or item.start_ns >= item.end_ns73                    or self.actual_window is None or item.end_ns > self.actual_window.end_ns):74                raise ValueError('时间事实未知范围无序、重叠或越界')75            cursor, unknown = item.end_ns, unknown + item.end_ns - item.start_ns76        if type(self.ranges_retained) is not bool or not self.ranges_retained and self.value_unknown_ranges:77            raise ValueError('精简时间事实不能携带未知范围')78        if self.ranges_retained and unknown + self.value_valid_duration_ns != span:79            raise ValueError('时间事实未完整划分有效和未知时长')80        # 原因、来源与身份不能由自由文字或路径占位替代。81        if not isinstance(self.quality, Q) or type(self.source_refs) is not tuple or any(not isinstance(ref, SourceRef) for ref in self.source_refs):82            raise TypeError('时间事实需要typed质量和完整来源tuple')83        require_text(self.binding_identity, 'time.binding_identity')848586@dataclass(frozen=True)87class TimeReductionResult:88    """保存整窗时间统计的数值快照,覆盖与report/strict由外层分类。8990    极值可为精确int;均值与时长为float。有效零和无有效证据的None分别保存。91    valid_term_count统计有效正区间,不等于真实更新样本数或TRUE区间数。92    """9394    value: int | float | None  # 最终单位下的有限候选值。95    unit: str  # 已核对的输出单位,不再逐块换算。96    calculation_failed: bool  # 权重、原序求和或最终转换故障。97    quality: Q  # 仅本次数值归约故障,输入原因在facts。98    valid_term_count: int  # 原值经算子门槛后有效的正区间数。99100    def __post_init__(self):101        """拒绝未知、正常零和故障之间的矛盾,不补候选值。102103        参数: 无。返回: None。异常: TypeError/ValueError为字段或状态矛盾。104        """105        require_text(self.unit, 'time.output_unit')106        # 精确整数无需float检查;bool与非有限浮点均不是有效统计输出。107        if self.value is not None and (type(self.value) not in (int, float)108                or type(self.value) is float and not isfinite(self.value)):109            raise ValueError('时间归约值需要有限int/float或None')110        if (type(self.calculation_failed) is not bool or not isinstance(self.quality, Q)111                or type(self.valid_term_count) is not int or self.valid_term_count < 0):112            raise TypeError('时间归约状态、质量和项数类型错误')113        # 数值故障须有明确证据,成功项不可无值,也不能混入数值故障原因。114        if self.calculation_failed:115            if self.value is not None or self.quality == Q.NONE or self.valid_term_count == 0:116                raise ValueError('时间计算故障需要有效项、None和质量原因')117        elif self.quality != Q.NONE or ((self.valid_term_count == 0) != (self.value is None)):118            raise ValueError('时间成功数值、项数或质量矛盾')119120121@dataclass(frozen=True)122class MeanTimeBlock:123    """借出一批均值所需数值和权重;不含原帧、双时间或逐条来源。124125    values/weights为参考tuple或NumPy有限列,调用者为其内存付费。126    仅均值首遍显式请求时生成;facts仍用于必要的结果分类。127    """128129    facts: object  # 无条件或条件时间事实。130    values: object  # 已通过均值精度门槛的有限数值列。131    weights: object  # 与values同长的正权重,周期模式为折算秒数。132133134def require_time_config(spec, input_unit, dtype, selected_duration_ns, partial_limit, *,135                        step_ns=None, selected_weight_seconds=None):136    """检查无条件时间归约配置,不执行数值转换或计算。137138    参数: 原spec/单位/dtype;selected_duration_ns为actual均值第二遍整窗时长;139        step_ns为固定/DBC周期,该模式均值改用selected_weight_seconds有效权重秒数。140    返回: CalculationConversion。异常: TypeError/ValueError为不支持能力或非法配置。141    """142    # 样本统计及条件选择有独立接口,不由时间统计配置承载。143    if not isinstance(spec, CalculationDefinition) or spec.operation not in ('min_time', 'max_time', 'time_weighted_mean', 'duration', *TIME_MOMENTS):144        raise TypeError('有限时间归约仅支持明确无条件时间算子')145    allowed = (D.BOOL,) if spec.operation == 'duration' else (D.INT64, D.UINT64, D.FLOAT64)146    if not isinstance(dtype, D) or dtype not in allowed:147        raise ValueError('时间算子与dtype不匹配')148    require_text(input_unit, 'time.input_unit')149    require_partial_limit(partial_limit)150    require_record_step(step_ns, ())151    if step_ns is not None and selected_duration_ns is not None:152        raise ValueError('周期均值不能使用实际纳秒时长作分母')153    if selected_weight_seconds is not None and (154            step_ns is None or spec.operation != 'time_weighted_mean'155            or type(selected_weight_seconds) is not float156            or not isfinite(selected_weight_seconds) or selected_weight_seconds <= 0):157        raise ValueError('仅周期均值第二遍接受有限正float权重秒数')158    # None明确表示均值第一遍;第二遍分母须正且可由安全int64时长表示。159    if selected_duration_ns is not None and (spec.operation != 'time_weighted_mean' or type(selected_duration_ns) is not int160            or not 0 < selected_duration_ns <= 2 ** 63 - 1):161        raise ValueError('仅时间均值第二遍接受正原生int64整窗有效时长')162    return calculation_conversion(spec, input_unit)163164165def require_time_series(series, spec, input_unit, dtype):166    """校验有限输入身份类型与正交集时长,不共享数值运算。167168    参数: series为有限CalculationSeries,其余为归约构造配置。169    返回: 当前实际交集Interval或None。异常: TypeError/ValueError/OverflowError。170    """171    # 只接受真实模型及无条件列,不自动转dtype、换单位或处理缺失结构。172    if not isinstance(series, CalculationSeries):173        raise TypeError('时间归约需要CalculationSeries')174    if series.conditions is not None or series.unit != input_unit or series.values[0].dtype is not dtype:175        raise ValueError('时间归约只接受同单位同dtype的无条件输入')176    left, right = max(spec.window.start_ns, series.observation.start_ns), min(spec.window.end_ns, series.observation.end_ns)177    if right - left > 2 ** 63 - 1:178        raise OverflowError('时间统计正交集时长超过int64')179    return Interval(left, right) if left <= right else None180181182def require_time_options(series, step_ns, valid_until_ns, retain_unknown_ranges):183    """检查记录周期、实际截止和范围输出开关,不执行数值计算。184185    参数: 有限series、可选周期、同长截止tuple和bool开关。返回: None。186    异常: 类型、长度或将合成边界当周期记录时拒绝。187    """188    require_record_step(step_ns, series.actual_value_updates)189    require_valid_until(series.times_ns, valid_until_ns)190    if type(retain_unknown_ranges) is not bool:191        raise TypeError('未知范围保留开关需要bool')192
Chronaxis 用户手册 · 离线阅读