Sign in

    moonisotonic

    纯 MoonBit 加权保序回归、稳健分位数拟合与可解释单调校准模型

    isotonic
    regression
    calibration
    pava
    Download zip
    Author
    Version
    0.1.0
    License
    MIT
    Last updated
    8 hours ago
    Downloads
    2

    #MoonIsotonic

    纯 MoonBit 的加权保序回归与可解释单调校准库。输入带权观测,输出满足递增或递减约束的拟合块和可移植经验模型;不依赖 Python、FFI 或第三方数值运行时。

    模块:kekeshuo/moonisotonic@0.1.0。许可证:MIT。项目是原创实现,不是 SciPy 或 scikit-learn 的逐行移植。

    #安装与最小使用

    在自己的 MoonBit 模块中执行:

    moon add kekeshuo/moonisotonic@0.1.0

    在调用包的 moon.pkg 中添加:

    import {
    "kekeshuo/moonisotonic" @iso,
    }

    let fit = @iso.fit_l2([3.0, 1.0, 2.0], [1.0, 1.0, 1.0]).unwrap()
    // fit.values == [2.0, 2.0, 2.0]; 块采用 [start, end) 下标。
    let model = @iso.train_l2([
    @iso.sample(0.0, 0.0),
    @iso.sample(2.0, 4.0),
    ]).unwrap()
    let prediction = @iso.predict(model, 1.0).unwrap() // 2.0
    let saved = @iso.encode_model(model).unwrap()
    let restored = @iso.decode_model(saved).unwrap()

    示例中的 unwrap() 只用于已知有效数据;生产调用应匹配 Ok / Err。完整公开接口见 pkg.generated.mbti,字段均有明确类型。

    #能力与接口

    工作流主要入口结果/约定
    有序数组拟合isotonic, fit_l2, fit_l1, fit_quantile权重严格为正,双方向,块与目标值
    带约束拟合fit_bounded, fit_range, check_bounds逐点有限闭区间,拒绝不可行链
    训练经验曲线prepare, train_l2, train_l1, train_quantile稳定按坐标排序,重复坐标共享预测
    查询/移植predict, predict_many, inverse, threshold_domain, JSON codec三种插值、三种边界策略、平台逆区间
    解释/导出audit_order, audit_projection, explain_fit, TSV原行恢复、违反定位、残差与块解释
    下游评估evaluate, reliability, holdout, cross_validate加权误差、Brier/ECE、显式分区
    描述不确定性train_band, band_coverage, resample_sensitivity不交叉经验分位数包络、调用者提供重采样计划

    weighted_quantile 采用下侧加权分位数:累计权重首次达到 tau×总权重的观测,tau 允许 0/1;拟合的 tau 必须严格在 (0,1)。L1 目标是加权绝对误差,不是其一半;quantile 目标为加权 pinball loss。

    #三个可运行下游场景

    1. 模型服务开发者:概率分数与标签按显式行索引划分训练/留出集,训练单调映射,对留出集比较 Brier/ECE,并保存 JSON 模型。留出误差不保证改善。 moon run examples/probability
    2. 仪表与传感器系统:将噪声递减标定点训练为递减曲线,查询读数与整个平台的逆区间,经 JSON 往返后验证预测一致。 moon run examples/instrument
    3. 数据分析工具:读取 x/y/weight 数值 TSV,拟合 L1 曲线和分位数包络,导出预测/残差与块解释,通过显式 fold IDs 生成 out-of-fold 预测。 moon run examples/analysis

    每个例子都包含输入、处理、结果断言,断言失败会退出。所有数据是仓库内人工合成夹具,无外部数据许可证或隐私数据。

    #验证与兼容

    已测开发工具链:moonc v0.10.14+7d59c7ec9-dev / moon 0.1.20260920。CI 固定官方该版本二进制和匹配 core,四目标均运行严格检查、构建、测试与三个示例。

    moon info && moon fmt moon fmt --check moon check --deny-warn --target wasm-gc moon test --deny-warn --target wasm-gc moon build --deny-warn --target wasm-gc moon run examples/probability --deny-warn --target wasm-gc # 将目标替换为 wasm、js、native 重复;native 需要 C 编译器。

    25 个测试声明包含 726 个 L2、2178 个 quantile、1638 个带界拟合独立穷举检查(不是 4542 个独立测试声明),另含黑盒 API、非法输入、变换性质、分区、编码和集成断言。没有性能优越性或统计有效性基准声明。docs/architecture.md 说明测试 oracle 与模型边界。

    #复杂度与限制

    • 链 L2 PAVA 与有限逐点 bounds 拟合:O(n) 时间、O(n) 空间。数据准备稳定排序 O(n log n)。
    • generalized PAVA 反复重排合并块,保守最坏 O(n² log n);不是线性 quantile 求解器。重复坐标分位数保留原始观测,不用均值替代。
    • 单次预测先 O(k) 验证模型再 O(log k) 查找;批量一次验证,O(k + q log k)。逆查询 O(k)。
    • 使用 Double,不处理任意精度或极端动态范围。非有限输入、总权重或目标溢出返回 Err;未提供区间算术或误差证书。
    • 默认线性插值和域外 Clamp;Previous/Next 和 Reject/Extend 显式选择。inverse 仅针对线性插值与训练域,返回闭区间,不逆推域外延长线。
    • JSON 是 moonisotonic/1 版本化数组 schema,不是其他框架的模型格式;模型/TSV 文本限制 16000000 UTF-16 code units。JSON 使用 core parser 的默认嵌套深度,另有限 knot 预算。
    • 模型公开数组可变;预测/编码入口重新验证,copy_model 隔离数组。错误是 String,不是稳定的机器错误码协议。
    • 固定宽度 ECE 依赖 bin_count;经验 quantile 包络和重采样敏感性不是置信区间。没有隐式随机拆分;行索引隔离不能保证没有实体、时间或特征泄漏。连续 fold 的训练仍包含其他所有 folds,不等于滚动回测。
    • 不实现一般 QP、任意偏序/DAG、多维保序、训练基模型、全统计工具箱、一般 CSV、CLI 文件管理、绘图、网络服务或 scikit-learn 全 API。

    #项目文档

    • docs/architecture.md:算法、模型、数值契约与验证方式
    • docs/security.md:不可信输入、预算与部署边界
    • docs/competition/duplicate-check.md:有界生态核查与相邻方案差异
    • REFERENCES.md、AI-USE.md:算法参考、原创与 AI 使用边界
    • CHANGELOG.md:版本变更

    比赛申报文件不属于公开源码或发布包。发布物只有库、测试、示例及去秘密的工程文档。

    BandCoverage

    pub(all) struct BandCoverage {
    count : Int
    weight : Double
    covered_weight : Double
    coverage : Double
    mean_width : Double
    } derive(
    Debug
    )

    BandCoverage::to_repr

    #deprecated("`BandCoverage::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn BandCoverage::to_repr(BandCoverage) ->
    Repr

    Block

    pub(all) struct Block {
    start : Int
    end : Int
    weight : Double
    value : Double
    } derive(
    Debug
    )

    Block::to_repr

    #deprecated("`Block::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn Block::to_repr(Block) ->
    Repr

    BlockExplanation

    pub(all) struct BlockExplanation {
    start : Int
    end : Int
    fitted : Double
    observed_mean : Double
    observed_min : Double
    observed_max : Double
    weight : Double
    squared_error : Double
    absolute_error : Double
    } derive(
    Debug
    )

    BlockExplanation::to_repr

    #deprecated("`BlockExplanation::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn BlockExplanation::to_repr(BlockExplanation) ->
    Repr

    Boundary

    pub(all) enum Boundary {
    Clamp
    Reject
    Extend
    } derive(
    Debug
    )

    Boundary::to_repr

    #deprecated("`Boundary::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn Boundary::to_repr(Boundary) ->
    Repr

    CalibrationComparison

    pub(all) struct CalibrationComparison {
    before : Reliability
    after : Reliability
    brier_change : Double
    ece_change : Double
    } derive(
    Debug
    )

    CalibrationComparison::to_repr

    #deprecated("`CalibrationComparison::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn CalibrationComparison::to_repr(CalibrationComparison) ->
    Repr

    CrossValidation

    pub(all) struct CrossValidation {
    predictions : Array[Double]
    aggregate : Evaluation
    folds : Array[Evaluation]
    } derive(
    Debug
    )

    CrossValidation::to_repr

    #deprecated("`CrossValidation::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn CrossValidation::to_repr(CrossValidation) ->
    Repr

    Direction

    pub(all) enum Direction {
    Increasing
    Decreasing
    } derive(
    Debug
    )

    Direction::to_repr

    #deprecated("`Direction::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn Direction::to_repr(Direction) ->
    Repr

    Evaluation

    pub(all) struct Evaluation {
    count : Int
    weight : Double
    mse : Double
    mae : Double
    bias : Double
    maximum_error : Double
    loss : Double
    } derive(
    Debug
    )

    Evaluation::to_repr

    #deprecated("`Evaluation::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn Evaluation::to_repr(Evaluation) ->
    Repr

    Fit

    pub(all) struct Fit {
    values : Array[Double]
    blocks : Array[Block]
    objective : Double
    direction : Direction
    } derive(
    Debug
    )

    Fit::to_repr

    #deprecated("`Fit::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn Fit::to_repr(Fit) ->
    Repr

    Holdout

    pub(all) struct Holdout {
    model : Model
    training : Evaluation
    validation : Evaluation
    predictions : Array[Double]
    validation_indices : Array[Int]
    } derive(
    Debug
    )

    Holdout::to_repr

    #deprecated("`Holdout::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn Holdout::to_repr(Holdout) ->
    Repr

    Interpolation

    pub(all) enum Interpolation {
    Linear
    Previous
    Next
    } derive(
    Debug
    )

    Interpolation::to_repr

    #deprecated("`Interpolation::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn Interpolation::to_repr(Interpolation) ->
    Repr

    InverseRange

    pub(all) struct InverseRange {
    lower : Double
    upper : Double
    } derive(
    Debug
    )

    InverseRange::to_repr

    #deprecated("`InverseRange::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn InverseRange::to_repr(InverseRange) ->
    Repr

    Knot

    pub(all) struct Knot {
    x : Double
    y : Double
    weight : Double
    } derive(
    Debug
    )

    Knot::to_repr

    #deprecated("`Knot::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn Knot::to_repr(Knot) ->
    Repr

    Loss

    pub(all) enum Loss {
    Squared
    Absolute
    Quantile(Double)
    } derive(
    Debug
    )

    Loss::to_repr

    #deprecated("`Loss::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn Loss::to_repr(Loss) ->
    Repr

    Model

    pub(all) struct Model {
    knots : Array[Knot]
    direction : Direction
    loss : Loss
    } derive(
    Debug
    )

    Model::to_repr

    #deprecated("`Model::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn Model::to_repr(Model) ->
    Repr

    OrderAudit

    pub(all) struct OrderAudit {
    violations : Array[Int]
    largest_drop : Double
    total_drop : Double
    plateaus : Int
    } derive(
    Debug
    )

    OrderAudit::to_repr

    #deprecated("`OrderAudit::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn OrderAudit::to_repr(OrderAudit) ->
    Repr

    Prepared

    pub(all) struct Prepared {
    samples : Array[Sample]
    original_indices : Array[Int]
    coordinates : Array[Double]
    offsets : Array[Int]
    means : Array[Double]
    weights : Array[Double]
    } derive(
    Debug
    )

    Prepared::to_repr

    #deprecated("`Prepared::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn Prepared::to_repr(Prepared) ->
    Repr

    ProjectionAudit

    pub(all) struct ProjectionAudit {
    residual_sum : Double
    min_prefix : Double
    max_prefix : Double
    max_block_balance : Double
    order : OrderAudit
    } derive(
    Debug
    )

    ProjectionAudit::to_repr

    #deprecated("`ProjectionAudit::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn ProjectionAudit::to_repr(ProjectionAudit) ->
    Repr

    QuantileBand

    pub(all) struct QuantileBand {
    lower : Model
    upper : Model
    lower_tau : Double
    upper_tau : Double
    } derive(
    Debug
    )

    QuantileBand::to_repr

    #deprecated("`QuantileBand::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn QuantileBand::to_repr(QuantileBand) ->
    Repr

    Reliability

    pub(all) struct Reliability {
    bins : Array[ReliabilityBin]
    brier : Double
    ece : Double
    maximum_gap : Double
    } derive(
    Debug
    )

    Reliability::to_repr

    #deprecated("`Reliability::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn Reliability::to_repr(Reliability) ->
    Repr

    ReliabilityBin

    pub(all) struct ReliabilityBin {
    index : Int
    count : Int
    weight : Double
    mean_prediction : Double
    event_rate : Double
    gap : Double
    } derive(
    Debug
    )

    ReliabilityBin::to_repr

    #deprecated("`ReliabilityBin::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn ReliabilityBin::to_repr(ReliabilityBin) ->
    Repr

    Sample

    pub(all) struct Sample {
    x : Double
    y : Double
    weight : Double
    } derive(
    Debug
    )

    Sample::to_repr

    #deprecated("`Sample::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn Sample::to_repr(Sample) ->
    Repr

    Sensitivity

    pub(all) struct Sensitivity {
    queries : Array[Double]
    lower : Array[Double]
    median : Array[Double]
    upper : Array[Double]
    fits : Int
    } derive(
    Debug
    )

    Sensitivity::to_repr

    #deprecated("`Sensitivity::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn Sensitivity::to_repr(Sensitivity) ->
    Repr

    audit_order

    fn audit_order(values : Array[Double], direction? : Direction, tolerance? : Double) -> Result[OrderAudit, String]

    audit_projection

    fn audit_projection(y : Array[Double], weights : Array[Double], fit : Fit) -> Result[ProjectionAudit, String]

    L2 chain optimality diagnostics. Increasing fits have nonpositive residual prefixes.

    band_coverage

    fn band_coverage(band : QuantileBand, samples : Array[Sample]) -> Result[BandCoverage, String]

    Descriptive empirical coverage, not a confidence guarantee.

    check_bounds

    fn check_bounds(lower : Array[Double], upper : Array[Double], direction? : Direction) -> Result[Unit, String]

    compare_calibration

    fn compare_calibration(samples : Array[Sample], result : Holdout, bin_count? : Int) -> Result[CalibrationComparison, String]

    Baseline score x must already be a probability. Negative change means improvement.

    contiguous_folds

    fn contiguous_folds(size : Int, fold_count : Int) -> Result[Array[Int], String]

    Caller-controlled chronological block folds, useful when random mixing is undesirable.

    copy_model

    fn copy_model(model : Model) -> Result[Model, String]

    Safe model copying for consumers retaining independently mutable arrays.

    cross_validate

    fn cross_validate(samples : Array[Sample], assignments : Array[Int], fold_count : Int, loss? : Loss, direction? : Direction) -> Result[CrossValidation, String]

    No shuffle, hidden random state or fold selection. Every row is validated once.

    decode_model

    fn decode_model(text : String, max_knots? : Int) -> Result[Model, String]

    Parse bounded, untrusted model data. No executable code or callbacks in the format.

    domain

    fn domain(model : Model) -> Result[(Double, Double), String]

    encode_model

    fn encode_model(model : Model) -> Result[String, String]

    Versioned data schema: [schema, direction, loss, quantile, [[x,y,weight],...]].

    evaluate

    fn evaluate(model : Model, samples : Array[Sample]) -> Result[Evaluation, String]

    evaluate_predictions

    fn evaluate_predictions(y : Array[Double], predictions : Array[Double], weights : Array[Double], loss? : Loss) -> Result[Evaluation, String]

    evaluation_lines

    fn evaluation_lines(evaluation : Evaluation) -> Array[String]

    expand_blocks

    fn expand_blocks(blocks : Array[Block], size : Int) -> Result[Array[Double], String]

    Expand a partition into fitted observations, rejecting inconsistent coverage.

    explain_fit

    fn explain_fit(y : Array[Double], weights : Array[Double], fit : Fit) -> Result[Array[BlockExplanation], String]

    explanation_table

    fn explanation_table(explanations : Array[BlockExplanation]) -> String

    fit_bounded

    fn fit_bounded(y : Array[Double], weights : Array[Double], lower : Array[Double], upper : Array[Double], direction? : Direction) -> Result[Fit, String]

    Exact L2 chain fit within individual finite intervals.

    fit_l1

    fn fit_l1(y : Array[Double], weights : Array[Double], direction? : Direction) -> Result[Fit, String]

    Absolute-error isotonic regression. Objective is L1, not half-L1 pinball loss.

    fit_l2

    fn fit_l2(y : Array[Double], weights : Array[Double], direction? : Direction) -> Result[Fit, String]

    Weighted least-squares projection onto a chain. Input order is significant.

    fit_quantile

    fn fit_quantile(y : Array[Double], weights : Array[Double], tau : Double, direction? : Direction) -> Result[Fit, String]

    Generalized PAVA with canonical lower minimizers. Sorting pooled blocks is deliberately simple, not linear-time.

    fit_range

    fn fit_range(y : Array[Double], weights : Array[Double], lower : Double, upper : Double, direction? : Direction) -> Result[Fit, String]

    group_replication_counts

    fn group_replication_counts(prepared : Prepared) -> Result[Array[Int], String]

    holdout

    fn holdout(samples : Array[Sample], training_indices : Array[Int], validation_indices : Array[Int], loss? : Loss, direction? : Direction) -> Result[Holdout, String]

    Explicit row identity prevents accidentally training on validation rows.

    inverse

    fn inverse(model : Model, target : Double) -> Result[InverseRange, String]

    Preimage under linear interpolation, restricted to the training domain.

    isotonic

    fn isotonic(y : Array[Double], direction? : Direction) -> Result[Fit, String]

    Equal-weight convenience API. Does not sort observations.

    knot_arrays

    fn knot_arrays(model : Model) -> Result[(Array[Double], Array[Double]), String]

    pinball_loss

    fn pinball_loss(y : Array[Double], values : Array[Double], weights : Array[Double], tau : Double) -> Result[Double, String]

    predict

    fn predict(model : Model, x : Double, interpolation? : Interpolation, boundary? : Boundary) -> Result[Double, String]

    predict_band

    fn predict_band(band : QuantileBand, queries : Array[Double]) -> Result[Array[InverseRange], String]

    predict_many

    fn predict_many(model : Model, queries : Array[Double], interpolation? : Interpolation, boundary? : Boundary) -> Result[Array[Double], String]

    Validate once per batch, then locate each query by binary search.

    prepare

    fn prepare(samples : Array[Sample]) -> Result[Prepared, String]

    Sort by x and then original row, preserving duplicates for nonquadratic fits.

    read_samples

    fn read_samples(text : String, max_rows? : Int) -> Result[Array[Sample], String]

    Numeric TSV, not general CSV. Exact x/y/weight header; no quoting or missing values.

    reliability

    fn reliability(labels : Array[Double], predictions : Array[Double], weights : Array[Double], bin_count? : Int) -> Result[Reliability, String]

    Fixed-width bins; exactly 1 belongs to the final bin. Empty bins are omitted.

    resample_sensitivity

    fn resample_sensitivity(samples : Array[Sample], plans : Array[Array[Int]], queries : Array[Double], lower_tau? : Double, upper_tau? : Double, loss? : Loss, direction? : Direction) -> Result[Sensitivity, String]

    Explicit resampling plans only. No random-number generation or confidence-level claim.

    restore_order

    fn restore_order(values : Array[Double], permutation : Array[Int]) -> Result[Array[Double], String]

    Restore a sorted vector to the caller's original sample order.

    sample

    fn sample(x : Double, y : Double, weight? : Double) -> Sample

    threshold_domain

    fn threshold_domain(model : Model, target : Double, above? : Bool) -> Result[InverseRange, String]

    train

    fn train(samples : Array[Sample], loss? : Loss, direction? : Direction) -> Result[Model, String]

    train_band

    fn train_band(samples : Array[Sample], lower_tau : Double, upper_tau : Double, direction? : Direction) -> Result[QuantileBand, String]

    train_l1

    fn train_l1(samples : Array[Sample], direction? : Direction) -> Result[Model, String]

    train_l2

    fn train_l2(samples : Array[Sample], direction? : Direction) -> Result[Model, String]

    L2 empirical calibration. Repeated x values are constrained to one fitted y.

    train_probability

    fn train_probability(samples : Array[Sample], direction? : Direction) -> Result[Model, String]

    train_quantile

    fn train_quantile(samples : Array[Sample], tau : Double, direction? : Direction) -> Result[Model, String]

    Fit grouped coordinates from raw replicates, not their arithmetic means.

    validate_model

    fn validate_model(model : Model) -> Result[Unit, String]

    validate_samples

    fn validate_samples(samples : Array[Sample]) -> Result[Unit, String]

    weighted_quantile

    fn weighted_quantile(y : Array[Double], weights : Array[Double], tau : Double) -> Result[Double, String]

    Lower weighted quantile, including deterministic endpoint and tie semantics.

    write_predictions

    fn write_predictions(samples : Array[Sample], model : Model) -> Result[String, String]

    write_samples

    fn write_samples(samples : Array[Sample]) -> Result[String, String]