403Webshell
Server IP : 35.80.110.71  /  Your IP : 216.73.216.221
Web Server : Apache/2.4.58 (Ubuntu)
System : Linux ip-172-31-21-44 6.17.0-1019-aws #19~24.04.1-Ubuntu SMP Tue Jun 23 18:53:06 UTC 2026 x86_64
User : ubuntu ( 1000)
PHP Version : 8.3.31
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/splitstream/current/scripts/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/splitstream/current/scripts/generate-stats-corpus.py
"""
stats/corpus/stats.json generator.

Produces a deterministic corpus of (sample, expected-result) tuples covering:

- Beta-Binomial conjugate updates at sample sizes from 10 to 1,000,000.
- Posterior CDF + quantile values for credible-interval computation across
  Beta(α, β) with α, β ∈ [1, 1e6], including extreme-tail params like
  Beta(2, 100000).
- Monte-Carlo P(A > B) for paired Beta distributions with a pinned RNG seed.
- Normal-Normal updates at varying prior strengths.
- Chi-squared p-value for 2x2 and 4x2 contingency tables for SRM.

The PHP analysis runs against the corpus in PHPUnit using math-php; the
TypeScript SDK runs the subset relevant to it in Vitest. Drift fails CI.

Pinned: scipy==1.13.*, numpy==1.26.*, Python 3.12.
"""

import json
import math
from pathlib import Path

import numpy as np
import scipy.stats as stats


def beta_binomial_rows():
    """Conjugate updates: posterior alpha/beta after observing (successes,
    trials) on a Beta(1, 1) prior."""
    cases = [
        (10, 100), (50, 1000), (100, 10000), (500, 5000),
        (1, 1), (0, 100), (100, 100),
        (5000, 50000), (250000, 500000), (200000, 1000000),
    ]
    rows = []
    for successes, trials in cases:
        rows.append({
            "name": f"BetaBinomial s={successes} n={trials}",
            "prior_alpha": 1.0,
            "prior_beta": 1.0,
            "successes": successes,
            "trials": trials,
            "expected_alpha": 1.0 + successes,
            "expected_beta": 1.0 + (trials - successes),
        })
    return rows


def beta_quantile_rows():
    cases = [
        (50, 50, 0.025), (50, 50, 0.975), (50, 50, 0.5),
        (1, 1, 0.5),
        (1001, 9001, 0.025), (1001, 9001, 0.975),
        (2, 100000, 0.5), (2, 100000, 0.975),
        (10001, 10001, 0.025), (10001, 10001, 0.975),
        (100, 9900, 0.025), (100, 9900, 0.975),
    ]
    rows = []
    for a, b, q in cases:
        rows.append({
            "name": f"Beta({a},{b}).ppf({q})",
            "alpha": a,
            "beta": b,
            "quantile": q,
            "expected_value": float(stats.beta.ppf(q, a, b)),
        })
    return rows


def beta_cdf_rows():
    cases = [
        (50, 50, 0.4026979166),
        (50, 50, 0.5973020834),
        (1001, 9001, 0.0942751131),
        (1001, 9001, 0.1060363320),
        (2, 100000, 0.0000557146),
    ]
    rows = []
    for a, b, x in cases:
        rows.append({
            "name": f"Beta({a},{b}).cdf({x})",
            "alpha": a,
            "beta": b,
            "x": x,
            "expected_value": float(stats.beta.cdf(x, a, b)),
        })
    return rows


def monte_carlo_rows():
    """P(B > A) for paired Beta distributions, computed with a pinned seed."""
    rng = np.random.default_rng(42)
    cases = [
        # (alphaA, betaA, alphaB, betaB) — control vs treatment posteriors
        (101, 901, 105, 895),     # treatment slight edge
        (1001, 9001, 1051, 8951), # treatment moderate edge, n~10k
        (501, 501, 505, 497),     # near-tie
        (101, 9901, 200, 9800),   # rare positive, larger treatment
    ]
    rows = []
    n_samples = 200_000
    for alphaA, betaA, alphaB, betaB in cases:
        a_draws = rng.beta(alphaA, betaA, size=n_samples)
        b_draws = rng.beta(alphaB, betaB, size=n_samples)
        p_b_gt_a = float(np.mean(b_draws > a_draws))
        rows.append({
            "name": f"MC P(B>A) Beta({alphaA},{betaA}) vs Beta({alphaB},{betaB})",
            "alpha_a": alphaA,
            "beta_a": betaA,
            "alpha_b": alphaB,
            "beta_b": betaB,
            "expected_p_b_gt_a": p_b_gt_a,
            "n_samples": n_samples,
            "tolerance_atol": 0.02,  # MC inherently has variance; PHP MC
                                     # uses a different RNG and 10k draws
                                     # vs the corpus's 200k → ~5e-3 SE,
                                     # 2e-2 leaves headroom
        })
    return rows


def chi_squared_rows():
    """SRM chi-squared p-values."""
    cases = [
        # (observed, expected_weights)
        ([5050, 4950], [0.5, 0.5]),           # near-balanced
        ([5000, 5000], [0.5, 0.5]),           # exactly balanced
        ([4800, 5200], [0.5, 0.5]),           # slight imbalance
        ([3500, 3500, 3000], [0.33, 0.33, 0.34]),  # 3-way
        ([2500, 2500, 2500, 2500], [0.25, 0.25, 0.25, 0.25]),  # 4-way
        ([5500, 4500], [0.5, 0.5]),           # SRM warning territory
    ]
    rows = []
    for observed, weights in cases:
        n = sum(observed)
        weight_sum = sum(weights)
        chi2 = 0.0
        for obs, w in zip(observed, weights):
            expected = (w / weight_sum) * n
            if expected > 0:
                chi2 += (obs - expected) ** 2 / expected
        df = len(observed) - 1
        p_value = float(stats.chi2.sf(chi2, df))
        rows.append({
            "name": f"SRM chi2 {observed} expect {weights}",
            "observed": observed,
            "expected_weights": weights,
            "expected_chi_squared": chi2,
            "expected_p_value": p_value,
        })
    return rows


def main():
    corpus = {
        "schema_version": 1,
        "description": "Cross-implementation stats engine corpus. PHP (math-php) and TS SDK round-trip these tuples in CI. Drift fails the build.",
        "generated_with": {
            "scipy": stats.__name__ + "==1.13.*",
            "numpy": "1.26.*",
            "python": "3.12",
            "rng_seed": 42,
        },
        "tolerances": {
            "conjugate_update": 1e-9,
            "quantile": 1e-7,
            "cdf": 1e-9,
            "monte_carlo": 2e-2,
            "chi_squared": 1e-7,
        },
        "beta_binomial_updates": beta_binomial_rows(),
        "beta_quantiles": beta_quantile_rows(),
        "beta_cdfs": beta_cdf_rows(),
        "monte_carlo": monte_carlo_rows(),
        "chi_squared": chi_squared_rows(),
    }

    output = Path(__file__).parent.parent / "stats" / "corpus" / "stats.json"
    output.parent.mkdir(parents=True, exist_ok=True)
    output.write_text(json.dumps(corpus, indent=2))
    print(f"Wrote {output}")
    print(f"  beta_binomial_updates: {len(corpus['beta_binomial_updates'])}")
    print(f"  beta_quantiles: {len(corpus['beta_quantiles'])}")
    print(f"  beta_cdfs: {len(corpus['beta_cdfs'])}")
    print(f"  monte_carlo: {len(corpus['monte_carlo'])}")
    print(f"  chi_squared: {len(corpus['chi_squared'])}")


if __name__ == "__main__":
    main()

Youez - 2016 - github.com/yon3zu
LinuXploit