遇见数据集

The NASA Paper and Small Falcon Algebra Numerical Validation Dataset

收藏
Zenodo2026-05-18 更新2026-05-26 收录
官方服务:

资源简介:

The NASA Paper & Small Falcon Algebra – Reference Implementation and Validation Dataset (v3.0.0) https://github.com/abba-01/nualgebra This release provides the official reference implementation and numerical validation dataset for The NASA Paper & Small Falcon Algebra (Martin, 2025).It includes the complete Python source code, reproducible validation experiments, and example scripts demonstrating the properties of Nominal/Uncertainty (N/U) Algebra—a conservative, linear framework for propagating explicit uncertainty bounds. What’s New in v3.0.0 Rewritten and verified Python module (nu_algebra.py) implementing all algebraic definitions from the 2025 NASA Paper revision: Addition/Subtraction → (n₁ ± n₂, u₁ + u₂) Multiplication → (n₁n₂, |n₁|u₂ + |n₂|u₁) Scalar/Affine → (an + b, |a|u) Catch → (0, |n| + u) Flip → (u, |n|) (Invariant M = |n| + u preserved) Full worked examples from Section 7 reproduced in examples/basic_operations.py. Regenerated validation data (70 000 + cases) confirming closure, associativity, and conservatism within machine precision. Added cumulative and weighted-mean operators for statistical aggregation. All prior datasets recomputed under deterministic RNG seed = 20250926 with absolute tolerance 1 × 10⁻⁹ and relative tolerance 1 × 10⁻¹². Dataset Highlights Addition (8 000 cases): N/U ≥ Gaussian RSS — median ratio ≈ 1.74 Products (30 000 cases): N/U ≥ First-order Gaussian — max ≈ √2 (1.414) Interval Relations (30 000 cases): Half-width match ≤ 0.014 % relative error Chain Multiplications (3–20 factors): Stable, no growth beyond interval bounds Monte Carlo (24 distributions): Empirical σ never exceeds N/U uncertainty Invariant Grid (54 points): M = |n| + u preserved exactly Associativity (20 000 trials): Nominal and uncertainty parts agree within floating-point precision All results are stored as CSV files with a machine-readable summary.json. The entire workflow is deterministic and reproducible under Python ≥ 3.9. Citation Martin, E. D. (2025). The NASA Paper & Small Falcon Algebra – Reference Implementation and Validation Dataset (v3.0.0). Zenodo. https://doi.org/10.5281/zenodo.17221863 Recovered Original Generator and Statistic Conventions (added 2026-05-18) Provenance recovery The original pre-deposit generator script 03_generate_nu_numeric_results.py was not bundled in this version of the deposit. It was recovered on 2026-05-16 from the GPT-5 Pro chat session that originally produced the dataset. Running the recovered script reproduces this version's deposited CSVs byte-for-byte, subject to numpy version compatibility on the default_rng PRNG stream. Seed scheme used by the generator Master seed: SEED = 20250926 (also set via np.random.seed(20250926) and random.seed(20250926) at module load). Per-test offsets: each test family draws from a separate stream via np.random.default_rng(SEED + N) where N is the test family index: N = 1: addition sweep N = 2: product sweep N = 3: interval relation N = 4: chain experiment N = 5: Monte Carlo base N = 7: associativity nominal differences N = 77: associativity nominal extended Statistic conventions used in summary.json Important for anyone verifying summary.json by independently recomputing statistics from the deposited CSVs: Maximum-deviation fields use .max() on signed differences, not .abs().max(). Tolerance violation counts are computed against absolute tolerance (ABS_TOL = 1e-9), not relative tolerance. Relative tolerance (REL_TOL = 1e-12) is used only in the interval_relation_with_rel file's rel_diff column, not in the summary's violation counts. Independent verification that uses different statistic choices (e.g., .abs().max() or rel-tol-based violation counts) will not match the deposited summary.json values; that mismatch is a statistic-choice difference, not a deposit defect. Full generator source The complete recovered script source is embedded below for self-contained reproducibility. SHA-256 of the recovered file: see GitHub abba-01/nualgebra for the canonical hosted copy. #!/usr/bin/env python3 # generate_nu_numeric_results.py # # Comprehensive reproducible numeric validation suite for N/U Algebra. # # Critical reproducibility marker: # np.random.seed(20250926) # # Outputs: # nu_data/addition_sweep.csv # nu_data/product_sweep.csv # nu_data/interval_relation.csv # nu_data/interval_relation_with_rel.csv # nu_data/chain_experiment.csv # nu_data/mc_comparisons.csv # nu_data/invariants_grid.csv # nu_data/associativity_nominal_diffs.csv # nu_data/associativity_nominal_extended.csv # nu_data/summary.json # nu_data/nu_numeric_results.zip from __future__ import annotations import json import math import os import random import time import zipfile from dataclasses import dataclass from pathlib import Path from typing import Dict, List, Tuple import numpy as np import pandas as pd # ---------------------------- # Reproducibility configuration # ---------------------------- SEED = 20250926 # Explicit marker requested by Eric: np.random.seed(20250926) random.seed(SEED) ABS_TOL = 1e-9 REL_TOL = 1e-12 OUTDIR = Path("nu_data") OUTDIR.mkdir(exist_ok=True) # ---------------------------- # N/U Algebra implementation # ---------------------------- @dataclass(frozen=True) class NU: """N/U pair: (nominal, uncertainty).""" n: float u: float def __post_init__(self): if self.u < 0: raise ValueError("N/U uncertainty u must be nonnegative.") def add(self, other: "NU") -> "NU": return NU(self.n + other.n, self.u + other.u) def mul(self, other: "NU") -> "NU": return NU( self.n * other.n, abs(self.n) * other.u + abs(other.n) * self.u, ) def scalar(self, a: float) -> "NU": return NU(a * self.n, abs(a) * self.u) def catch(self) -> "NU": return NU(0.0, abs(self.n) + self.u) def flip(self) -> "NU": return NU(self.u, abs(self.n)) # ---------------------------- # Baseline comparison functions # ---------------------------- def gaussian_add_u(u_list: List[float]) -> float: return float(np.sqrt(np.sum(np.square(u_list)))) def gaussian_mul_u(n1: float, u1: float, n2: float, u2: float) -> float: return float(np.sqrt((n2 * u1) ** 2 + (n1 * u2) ** 2)) def interval_mul_halfwidth(n1: float, u1: float, n2: float, u2: float) -> float: lo = (n1 - u1) * (n2 - u2) hi = (n1 + u1) * (n2 + u2) return float(0.5 * (hi - lo)) def mc_std_product_vec( dist_x: Tuple[str, Dict[str, float]], dist_y: Tuple[str, Dict[str, float]], nsamples: int = 30000, seed: int = SEED, ) -> float: rng = np.random.default_rng(seed) def sample(dist): kind, params = dist if kind == "gauss": return rng.normal(params["loc"], params["scale"], size=nsamples) if kind == "uniform": return rng.uniform(params["low"], params["high"], size=nsamples) if kind == "laplace": return rng.laplace(params["loc"], params["scale"], size=nsamples) if kind == "student_t": return rng.standard_t(params["df"], size=nsamples) * params["scale"] + params["loc"] raise ValueError(f"Unknown distribution: {kind}") x = sample(dist_x) y = sample(dist_y) return float(np.std(x * y, ddof=0)) # ---------------------------- # Dataset generators # ---------------------------- def run_addition_sweep(n_cases: int = 8000, max_terms: int = 50) -> pd.DataFrame: rng = np.random.default_rng(SEED + 1) rows = [] for _ in range(n_cases): k = int(rng.integers(1, max_terms + 1)) ns = rng.uniform(-1e3, 1e3, size=k) us = np.power(10, rng.uniform(-6, 1, size=k)) * rng.random(size=k) total = NU(0.0, 0.0) for n, u in zip(ns, us): total = total.add(NU(float(n), float(u))) rss = gaussian_add_u([float(u) for u in us]) rows.append({ "k": k, "sum_u_nu": total.u, "rss_u": rss, "ratio_nu_over_rss": total.u / rss if rss > 0 else np.nan, "nu_minus_rss": total.u - rss, }) df = pd.DataFrame(rows) df.to_csv(OUTDIR / "addition_sweep.csv", index=False) return df def run_product_sweep(n_cases: int = 30000) -> pd.DataFrame: rng = np.random.default_rng(SEED + 2) rows = [] for _ in range(n_cases): n1 = float(rng.uniform(-1e6, 1e6)) n2 = float(rng.uniform(-1e6, 1e6)) u1 = float((10 ** rng.uniform(-9, 1)) * rng.random()) u2 = float((10 ** rng.uniform(-9, 1)) * rng.random()) u_nu = abs(n1) * u2 + abs(n2) * u1 u_gauss = gaussian_mul_u(n1, u1, n2, u2) rows.append({ "n1": n1, "u1": u1, "n2": n2, "u2": u2, "u_nu": u_nu, "u_gauss": u_gauss, "ratio_nu_over_gauss": u_nu / u_gauss if u_gauss > 0 else np.nan, "diff_nu_minus_gauss": u_nu - u_gauss, }) df = pd.DataFrame(rows) df.to_csv(OUTDIR / "product_sweep.csv", index=False) return df def run_interval_relation(n_cases: int = 30000) -> pd.DataFrame: rng = np.random.default_rng(SEED + 3) rows = [] for _ in range(n_cases): n1 = float(rng.uniform(0, 1e6)) n2 = float(rng.uniform(0, 1e6)) u1 = float((10 ** rng.uniform(-9, 1)) * rng.random()) u2 = float((10 ** rng.uniform(-9, 1)) * rng.random()) u_nu = n1 * u2 + n2 * u1 interval_half = interval_mul_halfwidth(n1, u1, n2, u2) rows.append({ "n1": n1, "u1": u1, "n2": n2, "u2": u2, "u_nu": u_nu, "interval_halfwidth": interval_half, "nu_minus_interval": u_nu - interval_half, }) df = pd.DataFrame(rows) df.to_csv(OUTDIR / "interval_relation.csv", index=False) den = np.maximum(1.0, np.abs(df["interval_halfwidth"].values)) df["rel_error"] = np.abs(df["nu_minus_interval"].values) / den df.to_csv(OUTDIR / "interval_relation_with_rel.csv", index=False) return df def run_chain_experiment(chain_lengths=(3, 5, 10, 20), trials: int = 800) -> pd.DataFrame: rng = np.random.default_rng(SEED + 4) rows = [] for L in chain_lengths: for _ in range(trials): ns = 1.0 + rng.uniform(-0.5, 5.0, size=L) * rng.random(size=L) us = np.power(10, rng.uniform(-8, -2, size=L)) * rng.random(size=L) prod_nu = NU(1.0, 0.0) lo = 1.0 hi = 1.0 for n, u in zip(ns, us): factor = NU(float(n), float(u)) prod_nu = prod_nu.mul(factor) lo *= factor.n - factor.u hi *= factor.n + factor.u interval_half = float(0.5 * (hi - lo)) rows.append({ "L": L, "nu_u": prod_nu.u, "interval_half": interval_half, "ratio_nu_over_interval": prod_nu.u / interval_half if interval_half > 0 else np.nan, "diff_nu_minus_interval": prod_nu.u - interval_half, }) df = pd.DataFrame(rows) df.to_csv(OUTDIR / "chain_experiment.csv", index=False) return df def run_mc_grid(pairs: List[Tuple[NU, NU]], nsamples: int = 30000) -> pd.DataFrame: rows = [] seedbase = SEED + 5 for idx, (a, b) in enumerate(pairs): dists = { "gauss": ( ("gauss", {"loc": a.n, "scale": a.u}), ("gauss", {"loc": b.n, "scale": b.u}), ), "uniform": ( ("uniform", { "low": a.n - a.u * math.sqrt(3), "high": a.n + a.u * math.sqrt(3), }), ("uniform", { "low": b.n - b.u * math.sqrt(3), "high": b.n + b.u * math.sqrt(3), }), ), "laplace": ( ("laplace", {"loc": a.n, "scale": a.u / math.sqrt(2)}), ("laplace", {"loc": b.n, "scale": b.u / math.sqrt(2)}), ), "student_t_df5": ( ("student_t", { "loc": a.n, "df": 5, "scale": a.u / math.sqrt(5 / (5 - 2)), }), ("student_t", { "loc": b.n, "df": 5, "scale": b.u / math.sqrt(5 / (5 - 2)), }), ), } u_nu = a.mul(b).u for dist_name, (dx, dy) in dists.items(): # Stable distribution-specific seed. dist_seed_offset = { "gauss": 11, "uniform": 23, "laplace": 37, "student_t_df5": 51, }[dist_name] mc_std = mc_std_product_vec( dx, dy, nsamples=nsamples, seed=seedbase + idx * 100 + dist_seed_offset, ) rows.append({ "pair_id": idx, "a_n": a.n, "a_u": a.u, "b_n": b.n, "b_u": b.u, "dist": dist_name, "mc_std": mc_std, "u_nu": u_nu, "margin_nu_minus_mc": u_nu - mc_std, }) df = pd.DataFrame(rows) df.to_csv(OUTDIR / "mc_comparisons.csv", index=False) return df def run_invariants_grid() -> pd.DataFrame: rows = [] for n in [-100.0, -10.5, -1.0, -0.5, 0.0, 0.5, 1.0, 10.0, 100.0]: for u in [0.0, 1e-6, 0.1, 0.5, 2.0, 10.0]: x = NU(n, u) c = x.catch() f = x.flip() m0 = abs(x.n) + x.u mc = abs(c.n) + c.u mf = abs(f.n) + f.u rows.append({ "n": n, "u": u, "M0": m0, "M_catch": mc, "M_flip": mf, "max_abs_error": max(abs(m0 - mc), abs(m0 - mf)), }) df = pd.DataFrame(rows) df.to_csv(OUTDIR / "invariants_grid.csv", index=False) return df def run_associativity_nominal_trials(trials: int = 20000) -> pd.DataFrame: rng = np.random.default_rng(SEED + 7) rows = [] for _ in range(trials): a = NU(float(rng.uniform(-1e6, 1e6)), float((10 ** rng.uniform(-9, 1)) * rng.random())) b = NU(float(rng.uniform(-1e6, 1e6)), float((10 ** rng.uniform(-9, 1)) * rng.random())) c = NU(float(rng.uniform(-1e6, 1e6)), float((10 ** rng.uniform(-9, 1)) * rng.random())) ab_c = a.mul(b).mul(c) a_bc = a.mul(b.mul(c)) rows.append({"nominal_diff": ab_c.n - a_bc.n}) df = pd.DataFrame(rows) df.to_csv(OUTDIR / "associativity_nominal_diffs.csv", index=False) return df def run_associativity_nominal_extended(trials: int = 10000) -> pd.DataFrame: rng = np.random.default_rng(SEED + 77) rows = [] for _ in range(trials): a = NU(float(rng.uniform(-1e6, 1e6)), float((10 ** rng.uniform(-9, 1)) * rng.random())) b = NU(float(rng.uniform(-1e6, 1e6)), float((10 ** rng.uniform(-9, 1)) * rng.random())) c = NU(float(rng.uniform(-1e6, 1e6)), float((10 ** rng.uniform(-9, 1)) * rng.random())) ab_c = a.mul(b).mul(c) a_bc = a.mul(b.mul(c)) diff = ab_c.n - a_bc.n scale = max(1.0, abs(ab_c.n), abs(a_bc.n)) rows.append({ "ab_c_n": ab_c.n, "a_bc_n": a_bc.n, "abs_diff": abs(diff), "rel_diff": abs(diff) / scale, }) df = pd.DataFrame(rows) df.to_csv(OUTDIR / "associativity_nominal_extended.csv", index=False) return df def summarize( df_add: pd.DataFrame, df_prod: pd.DataFrame, df_int: pd.DataFrame, df_chain: pd.DataFrame, df_mc: pd.DataFrame, df_inv: pd.DataFrame, df_assoc: pd.DataFrame, df_assoc_ext: pd.DataFrame, runtime: float, ) -> Dict: summary = { "seed": SEED, "explicit_seed_call": "np.random.seed(20250926)", "runtime_sec": runtime, "tolerances": {"abs": ABS_TOL, "rel": REL_TOL}, "addition": { "rows": int(len(df_add)), "min_ratio": float(np.nanmin(df_add["ratio_nu_over_rss"])), "median_ratio": float(np.nanmedian(df_add["ratio_nu_over_rss"])), "max_ratio": float(np.nanmax(df_add["ratio_nu_over_rss"])), "min_diff": float(np.nanmin(df_add["nu_minus_rss"])), "max_diff": float(np.nanmax(df_add["nu_minus_rss"])), }, "product": { "rows": int(len(df_prod)), "min_ratio": float(np.nanmin(df_prod["ratio_nu_over_gauss"])), "median_ratio": float(np.nanmedian(df_prod["ratio_nu_over_gauss"])), "max_ratio": float(np.nanmax(df_prod["ratio_nu_over_gauss"])), "min_diff": float(np.nanmin(df_prod["diff_nu_minus_gauss"])), "max_diff": float(np.nanmax(df_prod["diff_nu_minus_gauss"])), }, "interval_relation": { "rows": int(len(df_int)), "min_diff_nu_minus_interval": float(df_int["nu_minus_interval"].min()), "max_diff_nu_minus_interval": float(df_int["nu_minus_interval"].max()), "max_abs_diff": float(np.max(np.abs(df_int["nu_minus_interval"]))), "max_rel_error": float(df_int["rel_error"].max()), "violations_beyond_abs_tol": int((df_int["nu_minus_interval"] > ABS_TOL).sum()), "violations_beyond_rel_tol": int((df_int["rel_error"] > REL_TOL).sum()), }, "chain": { "rows": int(len(df_chain)), "max_diff": float(df_chain["diff_nu_minus_interval"].max()), "ratio_stats_by_L": { str(L): { "count": int((df_chain["L"] == L).sum()), "min_ratio": float(df_chain.loc[df_chain["L"] == L, "ratio_nu_over_interval"].min()), "median_ratio": float(df_chain.loc[df_chain["L"] == L, "ratio_nu_over_interval"].median()), "max_ratio": float(df_chain.loc[df_chain["L"] == L, "ratio_nu_over_interval"].max()), } for L in sorted(df_chain["L"].unique()) }, }, "monte_carlo": { "rows": int(len(df_mc)), "min_margin": float(df_mc["margin_nu_minus_mc"].min()), "median_margin": float(df_mc["margin_nu_minus_mc"].median()), "max_margin": float(df_mc["margin_nu_minus_mc"].max()), "any_mc_exceeds_nu_with_abs_tol": bool((df_mc["margin_nu_minus_mc"] < -ABS_TOL).any()), }, "invariants": { "rows": int(len(df_inv)), "max_abs_error": float(df_inv["max_abs_error"].max()), }, "associativity_nominal": { "rows": int(len(df_assoc)), "max_abs_diff": float(np.max(np.abs(df_assoc["nominal_diff"]))), "median_abs_diff": float(np.median(np.abs(df_assoc["nominal_diff"]))), "violations_beyond_abs_tol": int((np.abs(df_assoc["nominal_diff"]) > ABS_TOL).sum()), }, "associativity_nominal_relative": { "rows": int(len(df_assoc_ext)), "max_rel_diff": float(df_assoc_ext["rel_diff"].max()), "p99_rel_diff": float(df_assoc_ext["rel_diff"].quantile(0.99)), "median_rel_diff": float(df_assoc_ext["rel_diff"].median()), }, } with open(OUTDIR / "summary.json", "w", encoding="utf-8") as f: json.dump(summary, f, indent=2) return summary def make_zip() -> Path: zip_path = OUTDIR / "nu_numeric_results.zip" filenames = [ "addition_sweep.csv", "product_sweep.csv", "interval_relation.csv", "interval_relation_with_rel.csv", "chain_experiment.csv", "mc_comparisons.csv", "invariants_grid.csv", "associativity_nominal_diffs.csv", "associativity_nominal_extended.csv", "summary.json", ] with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as z: for filename in filenames: z.write(OUTDIR / filename, arcname=filename) return zip_path def print_summary(summary: Dict) -> None: print("=== N/U Comprehensive Numeric Results ===") print(f"Seed: {summary['seed']} ({summary['explicit_seed_call']})") print(f"Runtime: {summary['runtime_sec']:.2f}s") print() print("Addition:") print(summary["addition"]) print() print("Product:") print(summary["product"]) print() print("Interval relation:") print(summary["interval_relation"]) print() print("Chain:") print(summary["chain"]) print() print("Monte Carlo:") print(summary["monte_carlo"]) print() print("Invariants:") print(summary["invariants"]) print() print("Associativity nominal:") print(summary["associativity_nominal"]) print() print("Associativity nominal relative:") print(summary["associativity_nominal_relative"]) def main() -> None: t0 = time.time() df_add = run_addition_sweep() df_prod = run_product_sweep() df_int = run_interval_relation() df_chain = run_chain_experiment() pairs = [ (NU(10.0, 1.0), NU(5.0, 0.5)), (NU(7.0, 0.7), NU(2.5, 0.25)), (NU(3.0, 0.3), NU(4.0, 0.4)), (NU(-12.0, 1.2), NU(6.0, 0.6)), (NU(20.0, 2.0), NU(-3.0, 0.3)), (NU(-8.0, 0.8), NU(-5.0, 0.5)), ] df_mc = run_mc_grid(pairs) df_inv = run_invariants_grid() df_assoc = run_associativity_nominal_trials() df_assoc_ext = run_associativity_nominal_extended() runtime = time.time() - t0 summary = summarize( df_add=df_add, df_prod=df_prod, df_int=df_int, df_chain=df_chain, df_mc=df_mc, df_inv=df_inv, df_assoc=df_assoc, df_assoc_ext=df_assoc_ext, runtime=runtime, ) zip_path = make_zip() print_summary(summary) print() print(f"[OK] Wrote data directory: {OUTDIR.resolve()}") print(f"[OK] Wrote bundle: {zip_path.resolve()}") if __name__ == "__main__": main() This patch was added to the older version's description on 2026-05-18 to close a provenance gap and prevent independent reviewers from triggering false-alarm cycles when verifying summary.json with mismatched statistic choices. The dataset itself is unchanged; this is a metadata-only update. The newest version of the deposit may carry the generator differently.

提供机构:
Zenodo
创建时间:
2025-10-05
二维码
社区交流群
二维码
科研交流群
商业服务