Chronaxis用户手册English
本页目录

chronaxis.core.conditional_integral_terms_numpy

源码基线:8904c2f744951857659abd40aaa3535d57faf53c

1"""用真实NumPy列运算生成有限条件积分面积与各自的覆盖事实。23输入有限数值CalculationSeries、显式BOOL条件与原integral请求,输出只读一维float644面积、bool掩码及ConditionalIntegralFacts。先按整数ns裁剪,再按TRUE/有效掩码乘秒。5数值转换、面积、故障和覆盖归约独立于标量参考;元数据循环只恢复诊断Interval。6FALSE、UNKNOWN不做乘法,不把未知存储零视为正常结果,不提前归约或换单位。7不修改输入、不读来源;N/N-1临时列及输出由调用方有限workspace预约并消费。8"""910# 只共享非数值模型门槛;计算采用独立NumPy转换与真实ufunc/掩码。11from dataclasses import dataclass12import numpy as np13from .conditional_integral_terms import ConditionalIntegralFacts, require_conditional_integral_terms14from .calculations import merge_ranges15from .models import Interval16from .numeric_numpy import DTYPES, NumericArray, to_float_numpy17from .types import QualityFlag as Q181920@dataclass(frozen=True)21class NumpyConditionalIntegralTerms:22    """保存原序只读面积与明确掩码,不借用可写输入或隐式掩码数组。"""2324    areas: np.ndarray  # float64形状(N-1,),未转换的输入单位乘秒。25    valid: np.ndarray  # bool形状(N-1,),仅TRUE且正常面积可归约。26    facts: ConditionalIntegralFacts  # 当前块双覆盖、样本与来源,不是全窗结果。2728    def __post_init__(self):29        """检查普通一维同长只读列、有限面积及冻结事实。3031        参数: 无。返回: None。异常: TypeError/ValueError为类型、形状、可写或占位错误。32        """33        # 拒绝自定义/隐藏掩码子类;纯输出不能依赖调用侧数组行为。34        if (type(self.areas) is not np.ndarray or type(self.valid) is not np.ndarray35                or self.areas.dtype != np.float64 or self.valid.dtype != bool36                or self.areas.ndim != 1 or self.areas.shape != self.valid.shape37                or not isinstance(self.facts, ConditionalIntegralFacts)):38            raise TypeError('数组条件积分项需要同长一维float64/bool及ConditionalIntegralFacts')39        if (self.areas.flags.writeable or self.valid.flags.writeable40                or not np.all(np.isfinite(self.areas)) or np.any(~self.valid & (self.areas != 0))):41            raise ValueError('数组条件积分项必须只读且有限,无效位置只能0存储')424344def compute_conditional_integral_terms_numpy(series, spec, *, step_ns=None, valid_until_ns=None,45                                           condition_valid_until_ns=None, retain_unknown_ranges=True):46    """按真实数组掩码生成面积,独立归约值、条件和选择覆盖。4748    参数: series为有限数值CalculationSeries及显式BOOL条件,spec为原integral请求。49        step_ns选择固定/DBC周期;两个until列给实际有效截止;范围明细可显式关闭。50    返回: NumpyConditionalIntegralTerms;只读面积/掩码形状N-1,临时列形状N或N-1。51    异常: 输入、单位、时间和分配错误传播;浮点故障写质量并使对应值/面积失效。52    """53    # 时间不先转float,实际交集为整数域;所有有限输入先过公共结构门槛。54    require_conditional_integral_terms(series, spec, step_ns=step_ns, valid_until_ns=valid_until_ns,55                                      condition_valid_until_ns=condition_valid_until_ns)56    if type(retain_unknown_ranges) is not bool:57        raise TypeError('未知范围保留选项需要bool')58    start = max(series.observation.start_ns, spec.window.start_ns)59    end = min(series.observation.end_ns, spec.window.end_ns)60    actual = Interval(start, end) if start <= end else None61    # N个真实typed值经独立数组精度核;无效0仅是受valid约束的存储。62    column = NumericArray(series.values[0].dtype,63        np.array([value.value if value.valid else 0 for value in series.values], dtype=DTYPES[series.values[0].dtype]),64        np.array([value.valid for value in series.values], dtype=bool),65        np.array([int(value.quality) for value in series.values], dtype=np.uint16))66    values = to_float_numpy(column)67    # BOOL列按已知和真值两个(N,)掩码处理,UNKNOWN占位False不进入FALSE覆盖。68    known = np.array([condition.valid for condition in series.conditions], dtype=bool)69    truth = np.array([condition.value if condition.valid else False for condition in series.conditions], dtype=bool)70    condition_quality = np.array([int(condition.quality) for condition in series.conditions], dtype=np.uint16)71    times = np.array(series.times_ns, dtype=np.int64)72    left, right = np.maximum(times[:-1], np.int64(start)), np.minimum(times[1:], np.int64(end))73    observed = right > left74    durations = np.zeros(len(left), dtype=np.int64)75    np.subtract(right, left, out=durations, where=observed)76    # 当前块副本可标记面积故障;只对TRUE有效正区间执行input_unit乘秒的ufunc。77    valid_values, quality_values = values.valid.copy(), values.quality.copy()78    value_right, condition_right = right, right79    value_durations, condition_durations = durations, durations80    # 两种有效性各有实际截止,不能用一个联合截止丢掉已知FALSE或值覆盖。81    for deadlines, is_value in ((valid_until_ns, True), (condition_valid_until_ns, False)):82        if deadlines is None:83            continue84        expiry = np.array([item if item is not None else (1 << 63) - 1 for item in deadlines], dtype=np.int64)85        has_expiry = np.array([item is not None for item in deadlines], dtype=bool)86        expired = has_expiry & (times >= expiry)87        clipped = np.maximum(left, np.minimum(right, expiry[:-1]))88        clipped_durations = np.zeros(len(left), dtype=np.int64)89        np.subtract(clipped, left, out=clipped_durations, where=observed)90        if is_value:91            valid_values[expired] = False92            quality_values[expired] |= int(Q.STALE)93            quality_values[:-1][observed & (clipped < right)] |= int(Q.STALE)94            value_right, value_durations = clipped, clipped_durations95        else:96            known[expired] = False97            condition_quality[expired] |= int(Q.STALE)98            condition_quality[:-1][observed & (clipped < right)] |= int(Q.STALE)99            condition_right, condition_durations = clipped, clipped_durations100    selected_durations = (durations if valid_until_ns is None and condition_valid_until_ns is None101                          else np.minimum(value_durations, condition_durations))102    keep = observed & (selected_durations > 0) & valid_values[:-1] & known[:-1] & truth[:-1]103    areas = np.zeros(len(left), dtype=np.float64)104    seconds = selected_durations.astype(np.float64)105    if step_ns is None:106        seconds /= 1_000_000_000107    else:108        # 分母保留完整实际记录对,不能改用条件/有效期裁剪后的长度。109        seconds /= np.diff(times)110        seconds *= step_ns / 1_000_000_000111    with np.errstate(over='ignore', under='ignore', invalid='ignore'):112        np.multiply(values.values[:-1], seconds, out=areas, where=keep)113    # 非有限及非零下溢只标实际执行的TRUE项,并使该位置真实样本失效。114    nonfinite = keep & ~np.isfinite(areas)115    underflow = keep & (areas == 0) & (values.values[:-1] != 0)116    quality_values[:-1][nonfinite] |= int(Q.NONFINITE)117    quality_values[:-1][underflow] |= int(Q.PRECISION_LOSS)118    valid_values[:-1][nonfinite | underflow] = False119    keep = observed & (selected_durations > 0) & valid_values[:-1] & known[:-1] & truth[:-1]120    areas[~keep] = 0.0121    # 五种覆盖分别按(N-1,)掩码归约正整数ns,总和不超过已验证int64实际跨度。122    value_duration = int(np.sum(value_durations, where=observed & valid_values[:-1], dtype=np.int64))123    known_duration = int(np.sum(condition_durations, where=observed & known[:-1], dtype=np.int64))124    selected_duration = int(np.sum(selected_durations, where=keep, dtype=np.int64))125    selected_unknown_duration = int(np.sum(condition_durations, where=observed & known[:-1] & truth[:-1], dtype=np.int64)) - selected_duration126    false_duration = int(np.sum(condition_durations, where=observed & known[:-1] & ~truth[:-1], dtype=np.int64))127    # 样本只计真实原值更新;条件变化与右终点不成为数值样本。128    actual_updates = np.array(series.actual_value_updates, dtype=bool)129    actual_samples = actual_updates & (times >= start) & (times < end)130    sample_count = int(np.count_nonzero(actual_samples & valid_values & known & truth))131    # 诊断来自实际正区间或真实样本,不将窗口外末点质量传播入结果。132    diagnostic = np.array([int(flag) for flag in series.diagnostics], dtype=np.uint16) | quality_values | condition_quality133    relevant = actual_samples.copy()134    relevant[:-1] |= observed135    quality = Q(int(np.bitwise_or.reduce(diagnostic, where=relevant, initial=0)))136    # Python循环仅将有限未知掩码恢复精确Interval;数值和覆盖未调用参考核。137    value_unknown = (tuple(Interval(int(value_right[index] if valid_values[index] else left[index]), int(right[index]))138        for index in np.flatnonzero(observed & (~valid_values[:-1] | (value_right < right)))) if retain_unknown_ranges else ())139    condition_unknown = (tuple(Interval(int(condition_right[index] if known[index] else left[index]), int(right[index]))140        for index in np.flatnonzero(observed & (~known[:-1] | (condition_right < right)))) if retain_unknown_ranges else ())141    facts = ConditionalIntegralFacts(spec, actual, value_duration, known_duration, selected_duration,142        selected_unknown_duration, false_duration, sample_count, merge_ranges(value_unknown),143        merge_ranges(condition_unknown), quality, series.unit, series.binding_identity, series.source_refs,144        ranges_retained=retain_unknown_ranges)145    # 返回只读列供独立原序float归约;输出未申请独立owner,不能脱离已预约workspace留存。146    areas.setflags(write=False)147    keep.setflags(write=False)148    return NumpyConditionalIntegralTerms(areas, keep, facts)149
Chronaxis 用户手册 · 离线阅读